diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2fe50038..59030fec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -387,6 +387,9 @@ jobs: run: bash script/ci/docs.sh install - name: Lint, type check, unit tests and content check run: bash script/ci/docs.sh check + - name: Translations complete (every page and README in every language) + if: ${{ !cancelled() }} + run: bash script/ci/docs.sh translations - name: Static build run: bash script/ci/docs.sh build diff --git a/README.de.md b/README.de.md index bcdb8ed5..01ba6e56 100644 --- a/README.de.md +++ b/README.de.md @@ -16,7 +16,7 @@ Einheitliche Berechtigungsplattform · Benutzer, Rollen, Menüs, APIs, Datenzeil Language: [English](README.md) · [中文说明](README.zh-CN.md) · [繁體中文](README.zh-TW.md) · [Русский](README.ru.md) · [한국어](README.ko.md) · [日本語](README.ja.md) · Deutsch · [Français](README.fr.md) · [Spanisch](README.es.md) · [Portugiesisch](README.pt-BR.md) · [Italienisch](README.it.md) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) -![Version](https://img.shields.io/badge/version-2026.1.0-4F46E5) +![Version](https://img.shields.io/badge/version-2026.4.0-4F46E5) ![Java](https://img.shields.io/badge/Java-17%2B-ED8B00) [![Docs](https://img.shields.io/badge/docs-grantforge.devlive.org-4F46E5)](https://grantforge.devlive.org) [![Docker](https://img.shields.io/badge/ghcr.io-grantforge-2496ED)](https://ghcr.io/devlive-community/grantforge) diff --git a/README.es.md b/README.es.md index 7a5df8e1..392e5d7b 100644 --- a/README.es.md +++ b/README.es.md @@ -16,7 +16,7 @@ Plataforma unificada de permisos · usuarios, roles, menús, API, filas y campos Language: [English](README.md) · [中文说明](README.zh-CN.md) · [繁體中文](README.zh-TW.md) · [Русский](README.ru.md) · [한국어](README.ko.md) · [日本語](README.ja.md) · [Deutsch](README.de.md) · [Français](README.fr.md) · Español · [Português](README.pt-BR.md) · [Italiano](README.it.md) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) -![Version](https://img.shields.io/badge/version-2026.1.0-4F46E5) +![Version](https://img.shields.io/badge/version-2026.4.0-4F46E5) ![Java](https://img.shields.io/badge/Java-17%2B-ED8B00) [![Docs](https://img.shields.io/badge/docs-grantforge.devlive.org-4F46E5)](https://grantforge.devlive.org) [![Docker](https://img.shields.io/badge/ghcr.io-grantforge-2496ED)](https://ghcr.io/devlive-community/grantforge) diff --git a/README.fr.md b/README.fr.md index 5823d8fc..9e581c02 100644 --- a/README.fr.md +++ b/README.fr.md @@ -16,7 +16,7 @@ Plateforme unifiée d’autorisations · utilisateurs, rôles, menus, API, ligne Language: [English](README.md) · [中文说明](README.zh-CN.md) · [繁體中文](README.zh-TW.md) · [Русский](README.ru.md) · [한국어](README.ko.md) · [日本語](README.ja.md) · [Deutsch](README.de.md) · Français · [Español](README.es.md) · [Português](README.pt-BR.md) · [Italiano](README.it.md) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) -![Version](https://img.shields.io/badge/version-2026.1.0-4F46E5) +![Version](https://img.shields.io/badge/version-2026.4.0-4F46E5) ![Java](https://img.shields.io/badge/Java-17%2B-ED8B00) [![Docs](https://img.shields.io/badge/docs-grantforge.devlive.org-4F46E5)](https://grantforge.devlive.org) [![Docker](https://img.shields.io/badge/ghcr.io-grantforge-2496ED)](https://ghcr.io/devlive-community/grantforge) diff --git a/README.it.md b/README.it.md index e6ea46f0..6d352255 100644 --- a/README.it.md +++ b/README.it.md @@ -16,7 +16,7 @@ Piattaforma unificata di permessi · utenti, ruoli, menu, API, righe e campi di Language: [English](README.md) · [中文说明](README.zh-CN.md) · [繁體中文](README.zh-TW.md) · [Русский](README.ru.md) · [한국어](README.ko.md) · [日本語](README.ja.md) · [Deutsch](README.de.md) · [Français](README.fr.md) · [Español](README.es.md) · [Português](README.pt-BR.md) · Italiano [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) -![Version](https://img.shields.io/badge/version-2026.1.0-4F46E5) +![Version](https://img.shields.io/badge/version-2026.4.0-4F46E5) ![Java](https://img.shields.io/badge/Java-17%2B-ED8B00) [![Docs](https://img.shields.io/badge/docs-grantforge.devlive.org-4F46E5)](https://grantforge.devlive.org) [![Docker](https://img.shields.io/badge/ghcr.io-grantforge-2496ED)](https://ghcr.io/devlive-community/grantforge) diff --git a/README.ja.md b/README.ja.md index ca85a645..ea6f542e 100644 --- a/README.ja.md +++ b/README.ja.md @@ -16,7 +16,7 @@ Language: [English](README.md) · [中文说明](README.zh-CN.md) · [繁體中文](README.zh-TW.md) · [Русский](README.ru.md) · [한국어](README.ko.md) · 日本語 · [Deutsch](README.de.md) · [フランス語](README.fr.md) · [スペイン語](README.es.md) · [ポルトガル語](README.pt-BR.md) · [イタリア語](README.it.md) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) -![Version](https://img.shields.io/badge/version-2026.1.0-4F46E5) +![Version](https://img.shields.io/badge/version-2026.4.0-4F46E5) ![Java](https://img.shields.io/badge/Java-17%2B-ED8B00) [![Docs](https://img.shields.io/badge/docs-grantforge.devlive.org-4F46E5)](https://grantforge.devlive.org) [![Docker](https://img.shields.io/badge/ghcr.io-grantforge-2496ED)](https://ghcr.io/devlive-community/grantforge) diff --git a/README.ko.md b/README.ko.md index 1cb8baa2..93fb3389 100644 --- a/README.ko.md +++ b/README.ko.md @@ -16,7 +16,7 @@ Language: [English](README.md) · [中文说明](README.zh-CN.md) · [繁體中文](README.zh-TW.md) · [Русский](README.ru.md) · [日本語](README.ja.md) · 한국어 · [Deutsch](README.de.md) · [프랑스어](README.fr.md) · [스페인어](README.es.md) · [포르투갈어](README.pt-BR.md) · [이탈리아어](README.it.md) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) -![Version](https://img.shields.io/badge/version-2026.1.0-4F46E5) +![Version](https://img.shields.io/badge/version-2026.4.0-4F46E5) ![Java](https://img.shields.io/badge/Java-17%2B-ED8B00) [![Docs](https://img.shields.io/badge/docs-grantforge.devlive.org-4F46E5)](https://grantforge.devlive.org) [![Docker](https://img.shields.io/badge/ghcr.io-grantforge-2496ED)](https://ghcr.io/devlive-community/grantforge) diff --git a/README.md b/README.md index abdfaddb..b25559c8 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ GrantForge (formerly AuthX) is an open-source (MIT) unified permission platform. | External systems | Plug-in service types and a policy engine: data services, access policies, agents and access auditing | | Delivery | One executable release, Docker image, Compose examples, Helm chart; H2, PostgreSQL, MySQL, MariaDB, Oracle, SQL Server | -External-system support covers the plug-in framework, policy editor, signed distribution and access auditing, with service plug-ins for HDFS, ZooKeeper, MySQL, Hive, Kafka, HBase, Trino, YARN, Pulsar, Solr, ClickHouse, Apache Doris, MongoDB, Redis, PostgreSQL, Elasticsearch and OpenSearch. The table below lists the versions verified in CI; each plug-in and agent guide has the details. +External-system support covers the plug-in framework, policy editor, signed distribution and access auditing, with service plug-ins for HDFS, ZooKeeper, MySQL, Hive, Kafka, HBase, Trino, YARN, Pulsar, Solr, ClickHouse, Apache Doris, MongoDB, Redis, PostgreSQL, Elasticsearch and OpenSearch. HDFS uses numbered NameNode agents, ZooKeeper an in-server agent, MySQL a standalone network proxy, Hive an authorizer in HiveServer2, Kafka an authorizer in the brokers and controllers, HBase a coprocessor in the masters and region servers, Trino a system access control in the coordinator, YARN an authorization provider in the ResourceManagers, Pulsar an authorization provider in the brokers, Solr an authorization plug-in in the nodes, and ClickHouse a service beside the servers that keeps ClickHouse's own roles and row policies in step. Apache Doris uses a service beside the cluster that keeps Doris's own roles, privileges and row policies in step, down to columns and workload groups. MongoDB uses a service beside the deployment that keeps MongoDB's own roles in step. Redis and Valkey use a service that keeps every user's ACL selectors in step on every node. PostgreSQL uses a service beside the servers that keeps PostgreSQL's own roles, privileges and row security policies in step. Elasticsearch uses a service beside the cluster that keeps Elasticsearch's own roles in step, with field- and document-level security. OpenSearch uses a service beside the cluster that keeps the security plug-in's own roles in step, with field- and document-level security and masked fields. Connection settings, database/table/column lookup and policy features are in the [MySQL plug-in guide](https://grantforge.devlive.org/en/plugins/mysql/); deployment and enforcement limits are in the [MySQL proxy agent guide](https://grantforge.devlive.org/en/external/mysql-agent/). Hive is covered by the [Hive plug-in guide](https://grantforge.devlive.org/en/plugins/hive/) and the [Hive agent guide](https://grantforge.devlive.org/en/external/hive-agent/). Kafka is covered by the [Kafka plug-in guide](https://grantforge.devlive.org/en/plugins/kafka/) and the [Kafka agent guide](https://grantforge.devlive.org/en/external/kafka-agent/). HBase is covered by the [HBase plug-in guide](https://grantforge.devlive.org/en/plugins/hbase/) and the [HBase agent guide](https://grantforge.devlive.org/en/external/hbase-agent/). Trino is covered by the [Trino plug-in guide](https://grantforge.devlive.org/en/plugins/trino/) and the [Trino agent guide](https://grantforge.devlive.org/en/external/trino-agent/). YARN is covered by the [YARN plug-in guide](https://grantforge.devlive.org/en/plugins/yarn/) and the [YARN agent guide](https://grantforge.devlive.org/en/external/yarn-agent/). Pulsar is covered by the [Pulsar plug-in guide](https://grantforge.devlive.org/en/plugins/pulsar/) and the [Pulsar agent guide](https://grantforge.devlive.org/en/external/pulsar-agent/). Solr is covered by the [Solr plug-in guide](https://grantforge.devlive.org/en/plugins/solr/) and the [Solr agent guide](https://grantforge.devlive.org/en/external/solr-agent/). ClickHouse is covered by the [ClickHouse plug-in guide](https://grantforge.devlive.org/en/plugins/clickhouse/) and the [ClickHouse agent guide](https://grantforge.devlive.org/en/external/clickhouse-agent/). Apache Doris is covered by the [Apache Doris plug-in guide](https://grantforge.devlive.org/en/plugins/doris/) and the [Apache Doris agent guide](https://grantforge.devlive.org/en/external/doris-agent/). MongoDB is covered by the [MongoDB plug-in guide](https://grantforge.devlive.org/en/plugins/mongodb/) and the [MongoDB agent guide](https://grantforge.devlive.org/en/external/mongodb-agent/). Redis is covered by the [Redis plug-in guide](https://grantforge.devlive.org/en/plugins/redis/) and the [Redis agent guide](https://grantforge.devlive.org/en/external/redis-agent/). PostgreSQL is covered by the [PostgreSQL plug-in guide](https://grantforge.devlive.org/en/plugins/postgresql/) and the [PostgreSQL agent guide](https://grantforge.devlive.org/en/external/postgresql-agent/). Elasticsearch is covered by the [Elasticsearch plug-in guide](https://grantforge.devlive.org/en/plugins/elasticsearch/) and the [Elasticsearch agent guide](https://grantforge.devlive.org/en/external/elasticsearch-agent/). OpenSearch is covered by the [OpenSearch plug-in guide](https://grantforge.devlive.org/en/plugins/opensearch/) and the [OpenSearch agent guide](https://grantforge.devlive.org/en/external/opensearch-agent/). ## Supported components and versions diff --git a/README.pt-BR.md b/README.pt-BR.md index bd489582..fe590b4e 100644 --- a/README.pt-BR.md +++ b/README.pt-BR.md @@ -16,7 +16,7 @@ Plataforma unificada de permissões · usuários, papéis, menus, APIs, linhas e Language: [English](README.md) · [中文说明](README.zh-CN.md) · [繁體中文](README.zh-TW.md) · [Русский](README.ru.md) · [한국어](README.ko.md) · [日本語](README.ja.md) · [Deutsch](README.de.md) · [Français](README.fr.md) · [Español](README.es.md) · Português · [Italiano](README.it.md) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) -![Version](https://img.shields.io/badge/version-2026.1.0-4F46E5) +![Version](https://img.shields.io/badge/version-2026.4.0-4F46E5) ![Java](https://img.shields.io/badge/Java-17%2B-ED8B00) [![Docs](https://img.shields.io/badge/docs-grantforge.devlive.org-4F46E5)](https://grantforge.devlive.org) [![Docker](https://img.shields.io/badge/ghcr.io-grantforge-2496ED)](https://ghcr.io/devlive-community/grantforge) diff --git a/README.ru.md b/README.ru.md index 46c0b860..118ff96b 100644 --- a/README.ru.md +++ b/README.ru.md @@ -16,7 +16,7 @@ Language: [English](README.md) · [中文说明](README.zh-CN.md) · [繁體中文](README.zh-TW.md) · Русский · [한국어](README.ko.md) · [日本語](README.ja.md) · [Deutsch](README.de.md) · [Французский](README.fr.md) · [Испанский](README.es.md) · [Португальский](README.pt-BR.md) · [Итальянский](README.it.md) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) -![Version](https://img.shields.io/badge/version-2026.1.0-4F46E5) +![Version](https://img.shields.io/badge/version-2026.4.0-4F46E5) ![Java](https://img.shields.io/badge/Java-17%2B-ED8B00) [![Docs](https://img.shields.io/badge/docs-grantforge.devlive.org-4F46E5)](https://grantforge.devlive.org) [![Docker](https://img.shields.io/badge/ghcr.io-grantforge-2496ED)](https://ghcr.io/devlive-community/grantforge) diff --git a/README.zh-CN.md b/README.zh-CN.md index 2d15dc4c..dcdf5af7 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -16,7 +16,7 @@ 语言:[English](README.md) · 中文 · [繁體中文](README.zh-TW.md) · [Русский](README.ru.md) · [한국어](README.ko.md) · [日本語](README.ja.md) · [Deutsch](README.de.md) · [法语](README.fr.md) · [西班牙语](README.es.md) · [葡萄牙语](README.pt-BR.md) · [意大利语](README.it.md) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) -![Version](https://img.shields.io/badge/version-2026.0.0-4F46E5) +![Version](https://img.shields.io/badge/version-2026.4.0-4F46E5) ![Java](https://img.shields.io/badge/Java-17%2B-ED8B00) [![Docs](https://img.shields.io/badge/docs-grantforge.devlive.org-4F46E5)](https://grantforge.devlive.org) [![Docker](https://img.shields.io/badge/ghcr.io-grantforge-2496ED)](https://ghcr.io/devlive-community/grantforge) @@ -71,9 +71,9 @@ GrantForge(原 AuthX)是一个开源(MIT)的统一权限平台。它集 | 资源调度 | Apache Hadoop YARN | `plugins/grantforge-plugin-yarn` | 3.2.4、3.3.6、3.4.3、3.5.0 | ResourceManager 授权提供者 | 与测试的 Hadoop 版本一致 | 8 | | 分布式协调 | Apache ZooKeeper | `plugins/grantforge-plugin-zookeeper` | 3.9.5 | 服务器内代理 | 3.6.4(编译依赖) | 8 | -[插件总览](https://grantforge.devlive.org/zh/plugins/overview/)对比了各代理执行策略的方式,以及哪些系统支持行过滤与屏蔽。 +[插件总览](https://grantforge.devlive.org/plugins/overview/)对比了各代理执行策略的方式,以及哪些系统支持行过滤与屏蔽。 -**代理 Java** 列为各代理编译的字节码目标版本。HDFS 代理按 Hadoop 版本线选择(例如 Hadoop 2.10.2 使用 `agents/hdfs/2.10/`),完整说明见 [HDFS 代理指南](https://grantforge.devlive.org/zh/external/hdfs-agent/)。Hadoop 2.7、2.10、3.2 与 3.3 不提供超级用户授权回调,因此这些版本线上的超级用户访问仍由 Hadoop 控制。 +**代理 Java** 列为各代理编译的字节码目标版本。HDFS 代理按 Hadoop 版本线选择(例如 Hadoop 2.10.2 使用 `agents/hdfs/2.10/`),完整说明见 [HDFS 代理指南](https://grantforge.devlive.org/external/hdfs-agent/)。Hadoop 2.7、2.10、3.2 与 3.3 不提供超级用户授权回调,因此这些版本线上的超级用户访问仍由 Hadoop 控制。 ## 工作原理:两个平面 diff --git a/README.zh-TW.md b/README.zh-TW.md index 80a888e6..e2d27d45 100644 --- a/README.zh-TW.md +++ b/README.zh-TW.md @@ -16,7 +16,7 @@ 語言:[English](README.md) · [简体中文](README.zh-CN.md) · [Русский](README.ru.md) · [한국어](README.ko.md) · [日本語](README.ja.md) · 繁體中文 · [Deutsch](README.de.md) · [法文](README.fr.md) · [西班牙文](README.es.md) · [葡萄牙文](README.pt-BR.md) · [義大利文](README.it.md) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) -![Version](https://img.shields.io/badge/version-2026.0.0-4F46E5) +![Version](https://img.shields.io/badge/version-2026.4.0-4F46E5) ![Java](https://img.shields.io/badge/Java-17%2B-ED8B00) [![Docs](https://img.shields.io/badge/docs-grantforge.devlive.org-4F46E5)](https://grantforge.devlive.org) [![Docker](https://img.shields.io/badge/ghcr.io-grantforge-2496ED)](https://ghcr.io/devlive-community/grantforge) diff --git a/core/grantforge-server/pom.xml b/core/grantforge-server/pom.xml index 5192bb96..d40e5d9d 100644 --- a/core/grantforge-server/pom.xml +++ b/core/grantforge-server/pom.xml @@ -22,6 +22,14 @@ GrantForge Server Spring Boot application that serves the REST API and the web console + + + true + + org.devlive.grantforge diff --git a/docs/content/de/architecture/overview.md b/docs/content/de/architecture/overview.md index e8f6ea15..1f6d7d54 100644 --- a/docs/content/de/architecture/overview.md +++ b/docs/content/de/architecture/overview.md @@ -86,6 +86,6 @@ sequenceDiagram | Persistenz | Hibernate 7 + Spring Data JPA; Liquibase-YAML-Migrationen; Hibernate prüft nur das Tabellenschema | | IDs | TSID (zeitgeordnete 64-Bit-IDs), nach außen immer als Zeichenkette übergeben | | Sitzungen | Spring Session JDBC, im Cluster gemeinsam genutzt | -| Frontend | Vue 3, Pinia, Vue Router, Tailwind CSS 4, Vite, TypeScript strict mode | +| Frontend | Vue 3, Pinia, Vue Router, Tailwind CSS 4, Vite, TypeScript im Strict-Modus | | Qualität | Error Prone + NullAway, Checkstyle, PMD, SpotBugs, ArchUnit, JaCoCo-Abdeckungsschwellen, ESLint, vue-tsc | | Tests | JUnit 5, jqwik, Testcontainers (sechs Datenbanken), Vitest, Playwright-Fullstack- und Beispiel-End-to-end-Tests, JMH und Benchmarks in Millionenhöhe | diff --git a/docs/content/de/changelog/2026.4.0.md b/docs/content/de/changelog/2026.4.0.md new file mode 100644 index 00000000..d5917bf6 --- /dev/null +++ b/docs/content/de/changelog/2026.4.0.md @@ -0,0 +1,68 @@ +--- +title: "2026.4.0" +description: "GrantForge 2026.4.0 (2026-11-08): Dienst-Plug-ins und Agenten für Elasticsearch, Apache Doris, MongoDB und Redis/Valkey, Synchronisierung der Organisationsstruktur aus DingTalk, direkte Benutzerberechtigungen, Export und Import des Ressourcenkatalogs und eine CI, die prüft, ob jede Sprache vollständig übersetzt ist." +--- + + +2026.4.0 erweitert die Berechtigungen externer Systeme auf Elasticsearch, Apache Doris, MongoDB sowie Redis und Valkey. Alle vier Agenten laufen neben dem System und gleichen dessen eigene Zugriffskontrolle mit den Richtlinien ab. Bei Identität und Autorisierung kommen die Synchronisierung der Organisationsstruktur aus DingTalk, direkte Benutzerberechtigungen sowie Export und Import des Ressourcenkatalogs hinzu. Alle Änderungen zeigt der [Vergleich auf GitHub](https://github.com/devlive-community/grantforge/compare/v2026.3.0...v2026.4.0). + +## Elasticsearch + +- Elasticsearch-Dienst-Plug-in: Indizes und Datenströme, ihre Felder und der Cluster; Anmeldung mit Passwort, API-Schlüssel oder TLS-Client-Zertifikat; siehe [Elasticsearch](/de/plugins/elasticsearch/). +- Agent neben dem Cluster: pflegt für jeden Benutzer eine eigene Elasticsearch-Rolle, zugewiesen an native Benutzer und über Rollenzuordnungen an Benutzer anderer Realms, mit Sicherheit auf Feld- und Dokumentebene, wo die Lizenz es erlaubt; Indexmuster bleiben Muster, sodass neue Indizes sofort erreichbar sind; siehe [Elasticsearch-Agent](/de/external/elasticsearch-agent/). +- Zugriffe stammen aus den Audit-Logdateien. Die Integrationstests decken Elasticsearch 8.19, 9.3, 9.4 und 9.5 ab. + +## Apache Doris + +- Doris-Dienst-Plug-in: Kataloge, Datenbanken, Tabellen, Spalten und Workload-Gruppen; verbindet sich mit einem oder mehreren FEs und meldet sich mit Passwort an; siehe [Apache Doris](/de/plugins/doris/). +- Agent neben dem Cluster: Richtlinien werden zu Doris' eigenen Rollen, Spaltenrechten, Rechten auf Workload-Gruppen und Zeilenrichtlinien und gelten für jede Identität des Benutzers; eine ganze Datenbank oder ein ganzer Katalog wird nur berechtigt, wenn Doris dadurch nicht mehr gewährt; siehe [Apache-Doris-Agent](/de/external/doris-agent/). +- Zugriffe stammen aus der Audit-Logtabelle von Doris. Die Integrationstests decken Doris 2.1, 3.1, 4.0 und 4.1 ab. + +## MongoDB + +- MongoDB-Dienst-Plug-in: Datenbanken und Collections; verbindet sich mit einem Replica Set oder einem Sharded Cluster und meldet sich per SCRAM, Client-Zertifikat, LDAP oder Kerberos an; siehe [MongoDB](/de/plugins/mongodb/). +- Agent neben dem Deployment: pflegt für jeden Benutzer eine MongoDB-Rolle und ersetzt ihre Privilegien in einem Schritt; anhand der geerbten Privilegien, die MongoDB meldet, zeigt er, was eingebaute und eigene Rollen außerhalb von GrantForge tatsächlich gewähren; siehe [MongoDB-Agent](/de/external/mongodb-agent/). +- Zugriffe stammen aus den Audit-Logdateien von Enterprise oder Percona oder aus dem Profiler. Die Integrationstests decken MongoDB 6.0, 7.0, 8.0 und 9.0 ab; Kerberos ist auf Percona Server for MongoDB geprüft. + +## Redis und Valkey + +- Redis-Dienst-Plug-in: Schlüssel, Kanäle und Knoten; verbindet sich mit einem einzelnen Server samt Replikaten oder mit einem Cluster und meldet sich als ACL-Benutzer an, wahlweise über TLS; siehe [Redis](/de/plugins/redis/). +- Agent neben dem Deployment: gleicht auf jedem Knoten die ACL-Selektoren jedes Benutzers mit den Richtlinien ab und ersetzt sie mit einem einzigen Befehl; die Grundrechte bleiben Sache der Betreiber, und der Agent fragt Redis mit `ACL DRYRUN`, ob sie Zugriffe erlauben, die die Richtlinien verweigern; siehe [Redis-Agent](/de/external/redis-agent/). +- Verweigerungen stammen aus dem ACL-Log jedes Knotens. Die Integrationstests decken Redis 7.2, 7.4, 8.2 und 8.10, Valkey 8.1 und 9.1 sowie einen Redis-8.10-Cluster ab. + +## Agenten + +- Die Agenten für ClickHouse, PostgreSQL und OpenSearch und die neuen Agenten dieser Version, die neben einem System laufen, teilen sich den neuen Kern `grantforge-agent-sync`: Start, Einstellungen, Timer, Synchronisierungsablauf, Lesepositionen im Audit und das Mitlesen von Logdateien. Jeder Agent behält nur, was zu seinem System gehört; Einstellungen und Logformate bleiben gleich. + +## Identität und Organisation + +- Datenquellen: Die Synchronisierung der Organisationsstruktur wird getrennt von den Identitätsquellen eingerichtet, der erste Adapter ist DingTalk mit dessen offiziellem Server-SDK; nach einer SSO-Anmeldung werden Benutzer über ihre vertrauenswürdige E-Mail-Adresse automatisch ihren Abteilungen und ihrer Hauptabteilung zugeordnet; die Tabellen werden auf allen sechs unterstützten Datenbanken migriert, SQL Server und Oracle eingeschlossen; siehe [Identitätsquellen (LDAP und OIDC)](/de/guide/identity-sources/). +- OpenID-Connect-Identitätsquellen können Telefonnummer und Avatar synchronisieren und der Autorisierungsanfrage Parameter hinzufügen (etwa `provider_hint` für Casdoor); die Parameter des Protokolls selbst lassen sich nicht überschreiben. + +## Autorisierung und Ressourcenkatalog + +- Direkte Benutzerberechtigungen: erlauben einem einzelnen Benutzer Menüs, Seiten, Schaltflächen oder APIs einer Fachanwendung, wahlweise bis zu einer Ablaufzeit; sie werden zusammen mit den Rollenberechtigungen ausgewertet, und Verweigerungen aus Rollen haben weiterhin Vorrang; siehe [Benutzer](/de/guide/users/). +- Der Ressourcenkatalog kann den Ressourcenbaum einer Anwendung als JSON exportieren und in eine andere Anwendung importieren: Der Import wird zuerst geprüft, und bei einem Konflikt wird nichts übernommen; Plattformadministratoren können geeignete Anwendungsbäume leeren; siehe [Ressourcen- und API-Katalog](/de/guide/catalog/). + +## Server + +- Beim Start zeigt der Server statt des Spring-Boot-Banners ein GrantForge-Banner mit seiner Version und der Java-Version. + +## Dokumentation und Veröffentlichung + +- Der Plug-in-Bereich beginnt mit einer Übersicht, gruppiert die Systeme nach Art in einer einklappbaren Seitenleiste und nennt für jedes System die Durchsetzung, die Unterstützung von Zeilenfiltern und Maskierung sowie die geprüften Versionen; alle READMEs führen die Komponenten in derselben Reihenfolge auf. +- Die CI prüft, ob jede Seite vollständig in jede Sprache übersetzt ist: dieselben Absätze, Überschriften, Tabellen, Codeblöcke und Links wie auf der chinesischen Seite, kein Text in der Schrift einer anderen Sprache und nichts unverändert von der englischen Seite übernommen; die 11 READMEs durchlaufen dieselben Prüfungen gegen das englische README. Alle fehlenden Übersetzungen, die die Prüfung gefunden hat, sind ergänzt. +- Die Versionsprüfung hält das Versionsabzeichen jedes READMEs aktuell. +- Die Workflows zum Veröffentlichen der Dokumentation und der Versionen laufen nicht mehr in Forks. +- Der Server `grantforge-server` wird nicht mehr in Maven-Repositorys veröffentlicht: Er bündelt alle Plug-ins und überschreitet deren Größengrenze; ausgeliefert wird er im Release-Archiv und im Image. + +## Fixes + +- Wird im Ressourceneditor der Typ einer Ressource geändert, wird der nicht mehr passende Ressourcencode geleert. +- Der letzte Codeblock der JavaScript-Integrationsanleitung verschluckt den folgenden Text nicht mehr, und die Konsolenadresse auf den Seiten zu Docker und zum Schnellstart macht den folgenden Text nicht mehr zum Link. +- Abhängigkeiten auf Versionen aktualisiert, die Sicherheitshinweise beheben: MariaDB Connector/J 3.5.11, OpenTelemetry 1.67.0, Guava 33.7.2, Snappy 1.1.10.9 sowie vue-i18n, glob und minimatch in Konsole und SDK. diff --git a/docs/content/de/deploy/docker.md b/docs/content/de/deploy/docker.md index 0ff28bec..cbabeef0 100644 --- a/docs/content/de/deploy/docker.md +++ b/docs/content/de/deploy/docker.md @@ -43,7 +43,7 @@ docker compose -f deploy/compose/postgres.yml up -d docker compose -f deploy/compose/postgres.yml logs grantforge | grep "setup token" ``` -Öffne dann http://127.0.0.1:9999/. Das in den Beispielen verwendete Standard-Datenbankpasswort eignet sich nur zum Ausprobieren; ändere es vor dem produktiven Einsatz über `GRANTFORGE_DB_PASSWORD`. Das MySQL-Beispiel erfordert, vorher `mysql-connector-j-.jar` nach `deploy/compose/drivers/` zu legen. +Öffne dann . Das in den Beispielen verwendete Standard-Datenbankpasswort eignet sich nur zum Ausprobieren; ändere es vor dem produktiven Einsatz über `GRANTFORGE_DB_PASSWORD`. Das MySQL-Beispiel erfordert, vorher `mysql-connector-j-.jar` nach `deploy/compose/drivers/` zu legen. ## Helm diff --git a/docs/content/de/develop/development.md b/docs/content/de/develop/development.md index 0f1c6772..0e0a6e6a 100644 --- a/docs/content/de/develop/development.md +++ b/docs/content/de/develop/development.md @@ -76,7 +76,7 @@ Das Skript verlangt einen sauberen Arbeitsstand, einen lokalen Zweig, der nicht - `script/ci/release.sh` baut das Release-Paket, eine CycloneDX-SBOM nur mit Release-Abhängigkeiten und `SHA256SUMS`; - Multi-Architektur-Images werden nach `ghcr.io/devlive-community/grantforge` gepusht; -- Maven-Artefakte (einschließlich Quellpaket und Javadoc) werden in GitHub Packages veröffentlicht; sind im Repository `CENTRAL_USERNAME`, `CENTRAL_PASSWORD` (Token des Central Portal), `GPG_PRIVATE_KEY` und `GPG_PASSPHRASE` gesetzt, werden sie signiert und nach Maven Central veröffentlicht; +- Maven-Artefakte (einschließlich Quellpaket und Javadoc) werden in GitHub Packages veröffentlicht; sind im Repository `CENTRAL_USERNAME`, `CENTRAL_PASSWORD` (Token des Central Portal), `GPG_PRIVATE_KEY` und `GPG_PASSPHRASE` gesetzt, werden sie signiert und nach Maven Central veröffentlicht; der Server `grantforge-server` bündelt alle Plug-ins und überschreitet die Größengrenze der Repositorys, daher kommt er in kein Maven-Repository und wird nur im Release-Archiv und im Image ausgeliefert; - Ein GitHub Release wird erzeugt, dessen Text alle Commits seit der vorherigen Veröffentlichung (`v*` oder ein numerisches Tag wie `1.0.6`) enthält, gruppiert nach neuen Funktionen, Fehlerbehebungen, Performance usw., mit Links zu den Commits. Release-Kandidaten werden als Vorabversion markiert und aktualisieren nicht das `latest`-Tag der Images. Lokal wird beim Aktivieren des `central`-Profils standardmäßig nichts veröffentlicht (`central.skip=true`); nur wenn der Release-Workflow `-Dcentral.skip=false` ausdrücklich übergibt, passiert es. @@ -100,3 +100,5 @@ pnpm build # Ausgabe nach docs/out ``` Die Seiten sind Markdown unter `docs/content/`, die Navigation steht in `docs/lib/navigation.ts`. API-Referenz und Fehlercodes werden beim Bauen aus Vertrag und Quellcode erzeugt. Screenshots entstehen mit `script/docs/screenshots.sh`: echter Dienststart, Beispieldaten einfügen und dann Playwright. + +`pnpm test` prüft auch die Übersetzungen: Jede chinesische Seite braucht eine Übersetzung in jede der zehn anderen Sprachen, mit denselben Absätzen, Überschriftenebenen, Listen, Tabellen, Codeblöcken und Links wie die chinesische Seite. Eine Seite darf weder Text in einer Schrift enthalten, die ihre Sprache nicht verwendet (auch vereinfachte Zeichen im traditionellen Chinesisch und im Japanischen), noch Sätze, die unverändert von der englischen Seite übernommen sind. Die 11 READMEs im Wurzelverzeichnis des Repositorys durchlaufen dieselben Prüfungen gegen das englische README, und ihre Links auf diese Website müssen auf vorhandene Seiten ihrer eigenen Sprache zeigen. diff --git a/docs/content/de/external/hdfs-agent.md b/docs/content/de/external/hdfs-agent.md index ce07b5a4..1341e7e1 100644 --- a/docs/content/de/external/hdfs-agent.md +++ b/docs/content/de/external/hdfs-agent.md @@ -9,71 +9,69 @@ description: GrantForge-Pfadrichtlinien mit dem zur Hadoop-Version passenden Nam project root for full license text. --> -Das Server-Plug-in `grantforge-plugin-hdfs` definiert Ressourcen und Verbindungen. Die nummerierten NameNode-Agenten für Hadoop 2.7, 2.10, 3.2, 3.3, 3.4 und 3.5 prüfen Zugriffe über die jeweilige Hadoop-SPI und teilen signierte Snapshots, Richtlinienauswertung und Audit-Übertragung. +Das Server-Plug-in `grantforge-plugin-hdfs` stellt den Diensttyp `hdfs`, seine Ressourcen und die Verbindungseinstellungen einheitlich bereit. Die NameNode-Agenten werden für jede Hadoop-Version eigens gebaut, prüfen Zugriffe über `INodeAttributeProvider` und `AccessControlEnforcer` der jeweiligen Version und teilen sich die Logik für signierte Richtlinien, Cache, Richtlinienauswertung und Audit. -Die gemeinsame HDFS-Logik liegt in `agents/grantforge-agent-hdfs-common`, die versionsabhängigen Adapter in `agents/grantforge-agent-hdfs-`. Die gemeinsamen nativen Adapter bilden das produktive Maven-Modul `agents/grantforge-agent-hdfs-native` mit Java 8 / Hadoop 2.7.7 als Basis. Die nummerierten Module nutzen dessen binäre Maven-Abhängigkeit und behalten ihre versionsabhängigen Einstiegspunkte und Callbacks, ohne die gemeinsamen Produktionsquellen erneut zu kompilieren. `core/grantforge-agent-core` bleibt die gemeinsame Protokoll- und Laufzeitbibliothek. +Im Quellcode liegt das Server-Plug-in in `plugins/grantforge-plugin-hdfs`, die Versions-Agenten liegen in `agents/grantforge-agent-hdfs-` und die gemeinsame Produktionslogik ohne Hadoop-Abhängigkeit in `agents/grantforge-agent-hdfs-common`. Die gemeinsame native Anbindung liegt im regulären Maven-Produktionsmodul `agents/grantforge-agent-hdfs-native` und richtet sich nach Java 8 / Hadoop 2.7.7; die nummerierten Module binden es als binäre Maven-Abhängigkeit ein, behalten ihre versionsspezifischen Einstiegspunkte und Callbacks und kompilieren den gemeinsamen Produktionscode nicht noch einmal. Protokoll, Snapshot-Cache und Audit-Infrastruktur liegen weiterhin in `core/grantforge-agent-core`. -Das Server-Plug-in behält einen `hdfs`-Diensttyp und eine von den Agenten unabhängige Client-Version. Verwende für Hadoop 2.x `webhdfs://namenode:50070` oder die entsprechende `swebhdfs://`-Adresse mit HTTPS; Hadoop 3.x kann RPC `hdfs://` oder WebHDFS nutzen. Die Container-Tests prüfen WebHDFS auf 2.x und beide Protokolle auf 3.x. RPC auf 2.x ist nicht zertifiziert; das konfigurierte Protokoll wird nicht automatisch gewechselt. +Das Server-Plug-in behält einen einzigen `hdfs`-Typ, und die Client-Version ist unabhängig von der Agent-Version. Verbindung und Pfadsuche nutzen bei Hadoop 2.x `webhdfs://namenode:50070` (mit HTTPS die entsprechende `swebhdfs://`-Adresse); bei Hadoop 3.x sind RPC über `hdfs://` und WebHDFS möglich. Die Container-Matrix prüft WebHDFS für 2.x sowie RPC und WebHDFS für 3.x; RPC für 2.x ist nicht zertifiziert, und das Verbindungsprotokoll wird nicht automatisch gewechselt. -Mit aktivierter Inode-Attributerweiterung löst Apache Hadoop 2.7.7 bei Abfragen von `/` durch normale Benutzer einen nativen Nullpointer-Fehler vor dem Agent-Callback aus. Verwende bei dieser Version tatsächliche Datenverzeichnisse wie `/data` für Datenoperationen und `lookup.path`. Der Containertest prüft diese Einschränkung ausdrücklich. +Mit aktivierter Inode-Attributerweiterung löst Apache Hadoop 2.7.7 bei einer direkten Abfrage des Wurzelpfads `/` durch einen normalen Benutzer im nativen Code einen Null-Pointer-Fehler aus, noch bevor der Agent aufgerufen wird. Nutze in dieser Version für Datenoperationen und den `lookup.path` des Dienstes echte Datenverzeichnisse wie `/data`; die Tests behalten eine eigene Prüfung für den Fehler am Wurzelpfad. -## Zielversionen der HDFS-Agenten +## Version wählen -| Hadoop-Basis | Java im Container | Agent-Verzeichnis | -| --- | --- | --- | -| 2.7.7 | Java 8 | `agents/hdfs/2.7/` | -| 2.10.2 | Java 8 | `agents/hdfs/2.10/` | -| 3.2.4 | Java 8 | `agents/hdfs/3.2/` | -| 3.3.6 | Java 8 (amd64-Image) | `agents/hdfs/3.3/` | -| 3.4.3 | Java 11 | `agents/hdfs/3.4/` | -| 3.5.0 | Java 17 | `agents/hdfs/3.5/` | +| Suffix des Agent-Moduls | Geprüftes Apache Hadoop | JVM im Container | Autorisierungsschnittstelle | Callback für Superuser-Pfade | +| --- | --- | --- | --- | --- | +| `2.7` | 2.7.7 | Java 8 | Parameter | nein | +| `2.10` | 2.10.2 | Java 8 | Parameter | nein | +| `3.2` | 3.2.4 | Java 8 | Parameter | nein | +| `3.3` | 3.3.6 | Java 8 | Kontext | nein | +| `3.4` | 3.4.3 | Java 11 | Kontext, Superuser- und Ablehnungs-Callbacks | ja | +| `3.5` | 3.5.0 | Java 17 | Kontext, Superuser- und Ablehnungs-Callbacks | ja | -Wähle für die Hadoop-Linie des Clusters `grantforge-agent-hdfs--.jar` aus `agents/hdfs//`. Die gemeinsame Logik zielt auf Java 8, der Adapter für 3.5 auf Java 17. +Für Hadoop 2.10.2 etwa wird `agents/hdfs/2.10/grantforge-agent-hdfs-2.10-.jar` installiert. Auf jedem NameNode wird genau ein versionsspezifischer Agent installiert; bei einem Upgrade wird das alte Jar entfernt, der Provider-Klassenname in der Konfiguration bleibt gleich. Beim Start prüft der Agent die Haupt- und Nebenversion von Hadoop sowie die benötigten Callbacks und bricht bei einer falschen Version ab; andere Patches derselben Haupt- und Nebenversion und Herstellerdistributionen müssen weiterhin eigens geprüft werden. -Hadoop 2.7, 2.10, 3.2 und 3.3 bieten den vom Agenten verwendeten Superuser-Autorisierungs-Callback nicht. Diese Superuserzugriffe bleiben unter der Kontrolle von Hadoop; für Datenzugriffe mit GrantForge-Richtlinien sollten normale Benutzer verwendet werden. +Der Hauptcode der Agenten `common` und `2.7` bis `3.4` verwendet Java-8-Bytecode, `3.5` Java 17. Die Tabelle nennt die JVMs, mit denen die Integrationstests tatsächlich laufen; sie besagt nicht, dass 3.4.3 mit Java 8 geprüft ist. Das offizielle Testimage für 3.3.6 gibt es nur für amd64, daher läuft es auf ARM-Hosts ausdrücklich in amd64-Emulation. ## Verhältnis zu den nativen Berechtigungen -Das folgende Durchsetzungsverhalten beschreibt Zugriffe normaler Benutzer. Die Einschränkungen für Superuser sind oben und im Abschnitt zu den Superuser-Callbacks beschrieben. - -Der Agent führt zuerst die nativen HDFS-Berechtigungsprüfungen aus, danach die GrantForge-Richtlinien: Ein Benutzer muss sowohl die nativen Berechtigungen als auch die Richtlinien erfüllen. Erlauben-Richtlinien von GrantForge umgehen weder POSIX-Berechtigungen, ACLs, Eigentümerprüfungen noch das Sticky-Bit; Ablehnen-Richtlinien lehnen immer ab. Native Berechtigungseinstellungen werden weiterhin über Hadoops Verwaltungswerkzeuge gepflegt. +Der Agent führt zuerst die nativen HDFS-Berechtigungsprüfungen aus und wendet danach die GrantForge-Richtlinien an: Ein Benutzer muss sowohl die nativen Berechtigungen als auch die Richtlinien erfüllen. Erlaubende Richtlinien von GrantForge umgehen weder POSIX-Rechte noch ACLs, Eigentümerprüfungen oder das Sticky Bit; verweigernde Richtlinien verweigern immer. Die nativen Berechtigungen werden weiterhin mit den Verwaltungswerkzeugen von Hadoop gepflegt. -Standard ist `grantforge.hdfs.native.fallback=false`: Ohne lokales Richtlinien-Snapshot, ohne passende Richtlinie oder bevor der Agent gestartet ist, wird der Datenzugriff abgelehnt. Mit `true` fallen Zugriffe, die keine Richtlinie entscheidet, auf die nativen Berechtigungen zurück; explizite Ablehnen-Richtlinien gelten weiterhin. Solange der Server vorübergehend nicht erreichbar ist, wird weiterhin das letzte lokale Snapshot verwendet, dessen Signaturprüfung erfolgreich war. +Standard ist `grantforge.hdfs.native.fallback=false`: Ohne lokalen Richtlinien-Snapshot, ohne passende Richtlinie oder solange der Agent noch nicht gestartet ist, werden Datenzugriffe verweigert. Mit `true` entscheiden bei Zugriffen, die keine Richtlinie regelt, die nativen Berechtigungen; ausdrückliche Verweigerungen gelten weiterhin. Ist der Server vorübergehend nicht erreichbar, wird der letzte lokale Snapshot mit gültiger Signatur weiter verwendet. -Vorfahren, Ziel, Teilbäume und Snapshot-Pfadprojektionen innerhalb eines einzigen Autorisierungs-Callbacks verwenden dieselbe Version des Richtlinien-Snapshots; aktualisierte Richtlinien wirken ab dem nächsten Callback, damit Erlauben-Regeln verschiedener Versionen nie kombiniert werden. Zugriffs-Audits protokollieren die tatsächlich verwendete Richtlinienversion. +Vorfahren, Ziel, Teilbäume und Snapshot-Pfadprojektionen innerhalb eines Autorisierungs-Callbacks verwenden dieselbe Version des Richtlinien-Snapshots; aktualisierte Richtlinien gelten ab dem nächsten Callback, sodass sich keine erlaubenden Regeln verschiedener Versionen mischen. Das Zugriffs-Audit vermerkt die tatsächlich verwendete Richtlinienversion. -Der Agent prüft die `read`-, `write`- und `execute`-Rechte, die ein normaler Benutzer für das Ziel benötigt, sowie die übergeordneten Verzeichnisse, die Vorfahrenverzeichnisse und die Unterverzeichnisse, die eine rekursive Prüfung erfordern. Vorgänge wie Erstellen, Löschen und Umbenennen betreffen mehrere Pfade, und Erlauben-Richtlinien müssen sie alle abdecken. Im strikten Modus reicht eine `read`-Richtlinie auf die Zieldatei allein nicht: Der Benutzer braucht auch `execute`-Richtlinien auf den Vorfahrenverzeichnissen; erlaube zum Beispiel `execute` auf `/` mit gesetzter Rekursion und konfiguriere dann Lese- und Schreibrechte auf den tatsächlichen Datenverzeichnissen. +Der Agent prüft die `read`-, `write`- und `execute`-Rechte, die ein normaler Benutzer für das Ziel braucht, sowie Elternverzeichnis, Vorfahrenverzeichnisse und rekursiv zu prüfende Unterverzeichnisse. Anlegen, Löschen, Umbenennen und ähnliche Operationen betreffen mehrere Pfade, und erlaubende Richtlinien müssen sie alle abdecken. Im strikten Modus genügt eine `read`-Richtlinie auf die Zieldatei nicht; der Benutzer braucht auch `execute`-Richtlinien auf den Vorfahrenverzeichnissen, etwa `execute` auf `/` mit Rekursion, und dann Lese- und Schreibrechte auf den eigentlichen Datenverzeichnissen. -Snapshot-Pfade werden sowohl als tatsächlich angefragter Pfad als auch als ursprünglicher Pfad ohne `.snapshot/` geprüft, zum Beispiel wird `/data/.snapshot/s1/secret` auch als `/data/secret` geprüft. Ablehnen-Richtlinien auf dem ursprünglichen Pfad gelten damit auch für Snapshots; für explizite Snapshot-Pfade können zusätzlich strengere Einschränkungen gesetzt werden. Metadaten-Abfragen folgen der Berechtigungssemantik der HDFS-Verzeichnisdurchquerung. +Snapshot-Pfade werden sowohl als tatsächlich angefragter Pfad als auch als ursprünglicher Pfad ohne `.snapshot/` geprüft; für `/data/.snapshot/s1/secret` wird also auch `/data/secret` geprüft. Verweigernde Richtlinien auf dem ursprünglichen Pfad gelten daher auch für Snapshots; für ausdrückliche Snapshot-Pfade lassen sich zusätzlich strengere Einschränkungen setzen. Metadatenabfragen folgen der HDFS-Semantik für das Durchlaufen von Verzeichnissen. -Eine einzige rekursive Autorisierung prüft höchstens `100000` Inodes; jenseits dieser Grenze wird der Vorgang abgelehnt, um unbegrenzte Speicherzuweisung im Inneren des NameNode zu vermeiden. Überlange Pfade werden mit dem vollständigen Pfad bewertet; die Anzeige der Audit-Ressource ist auf `1000` Zeichen begrenzt, und die ursprüngliche Länge sowie ein SHA-256-Digest werden in den Anfragedetails protokolliert. +Eine einzelne rekursive Autorisierung prüft höchstens `100000` Inodes; darüber wird die Operation verweigert, damit der NameNode nicht unbegrenzt Speicher belegt. Sehr lange Pfade werden anhand des vollständigen Pfads gegen die Richtlinien geprüft; die Anzeige der Ressource im Audit ist auf `1000` Zeichen begrenzt, und die Anfragedetails vermerken die ursprüngliche Länge und einen SHA-256-Digest. -Die folgenden Superuser-Callbacks gelten für Hadoop 3.4 und 3.5. HDFS-Superuser werden weiterhin von Hadoop verwaltet. Superuser-Callbacks mit Pfad bestehen zuerst Hadoops Superuser-Prüfung und werden dann anhand der von Hadoop 3.4 / 3.5 bereitgestellten Vorgangsnamen gegen die Richtlinien geprüft: Dateilesen und Metadaten-Abfragen erfordern `read`, Verzeichnisaufzählung erfordert `read` + `execute`, bekannte Änderungsvorgänge erfordern `write`. Unbekannte, fehlende oder nicht zuverlässig ableitbare Vorgänge (zum Beispiel `checkAccess` und `concat`) erfordern sicherheitshalber alle drei. +Hadoop 2.7, 2.10, 3.2 und 3.3 lassen Superuser vor dem Aufruf des Agenten passieren; diese Zugriffe kann der Agent weder steuern noch protokollieren. Bei Hadoop 3.4 und 3.5 durchlaufen Superuser-Callbacks mit Pfad zuerst die native Prüfung, danach werden die Richtlinien nach dem Operationsnamen geprüft: Lesen von Dateien und Metadatenabfragen verlangen `read`, das Auflisten von Verzeichnissen `read` + `execute` und bekannte ändernde Operationen `write`. Unbekannte, fehlende oder nicht sicher bestimmbare Operationen (etwa `checkAccess` und `concat`) verlangen vorsichtshalber alle drei Rechte. -Superuser-Callbacks haben keinen vollständigen Inode- und Teilbaumkontext, und pfadlose Clusterverwaltungsaufrufe behalten die native Prüfung; keiner der rekursiven Vorgänge eines Superusers lässt sich mit Unterverzeichnisrichtlinien einschränken. Datennutzer sollten reguläre Hadoop-Benutzer verwenden. +Superuser-Callbacks haben keinen vollständigen Inode- und Teilbaumkontext; Cluster-Verwaltungsaufrufe ohne Pfad bleiben bei der nativen Prüfung, und nicht alle rekursiven Operationen eines Superusers lassen sich mit Richtlinien auf Unterverzeichnissen einschränken. Wer Daten nutzt, sollte normale Hadoop-Benutzer verwenden. ## Metriken -Der Agent meldet Metriken über Hadoops Metrics2-System, über dieselben Sinks wie die dfs-Metriken des NameNode selbst; im JMX des NameNode lauten sie `Hadoop:service=NameNode,name=GrantForgeHdfsAgent` (ein Prometheus-JMX-Exporter kann sie direkt abgreifen). Jede Metrik trägt die Labels `instance` (Name der Datendienstinstanz) und `agentVersion`, sodass die beiden NameNodes eines HA-Paars unterscheidbar sind. Eine fehlgeschlagene Registrierung kostet nur die Metriken selbst: Der Agent protokolliert eine Warnung und setzt die Autorisierung ohne sie fort. +Der Agent meldet Metriken über das Metrics2-System von Hadoop und verwendet dieselben Sinks wie die dfs-Metriken des NameNodes; im JMX des NameNodes erscheinen sie als `Hadoop:service=NameNode,name=GrantForgeHdfsAgent` (der JMX-Exporter von Prometheus kann sie direkt abgreifen). Jede Metrik trägt die Labels `instance` (Name der Datendienst-Instanz) und `agentVersion`, sodass sich die beiden NameNodes eines HA-Paars getrennt betrachten lassen. Schlägt die Registrierung fehl, gehen nur die Metriken verloren: Der Agent protokolliert eine Warnung und autorisiert ohne Metriken weiter. | Metrik | Bedeutung | | --- | --- | -| `Callbacks` | Autorisierungs-Callbacks, die der Agent ausgeführt hat | -| `SuperuserCallbacks` | Superuser-Autorisierungs-Callbacks, die der Agent ausgeführt hat (3.4 / 3.5) | -| `NativeDenies` | Zugriffe, die Hadoop abgelehnt hat, bevor der Agent zum Zug kam | -| `EvaluationFailures` | Callbacks, die wegen einer Ausnahme bei der Richtlinienauswertung fehlschlugen und den Zugriff ablehnten (fail closed) | -| `DecisionsAllowed` / `DecisionsDenied` / `DecisionsUndetermined` | Von einer Richtlinie erlaubte / abgelehnte / unentschiedene Rechte; unentschieden wird im strikten Modus ebenfalls abgelehnt | -| `MissingSnapshots` | Bediente Callbacks, während kein verifiziertes Richtlinien-Snapshot vorlag | -| `SnapshotVersion` | Richtlinienversion des aktuell verwendeten Snapshots, 0 bedeutet keine | -| `QueuedEvents` / `DroppedEvents` | Audit-Ereignisse, die im Speicher auf die Meldung warten / wegen voller Queue oder vollem Puffer verworfen wurden | -| `ServerReachable` | Ob der letzte Zugriff auf den Richtlinien-Server erfolgreich war (1/0) | +| `Callbacks` | Vom Agenten ausgeführte Autorisierungs-Callbacks | +| `SuperuserCallbacks` | Vom Agenten ausgeführte Superuser-Callbacks | +| `NativeDenies` | Zugriffe, die Hadoop schon vor dem Agenten verweigert hat | +| `EvaluationFailures` | Callbacks, die wegen eines Fehlers bei der Richtlinienauswertung verweigert wurden | +| `DecisionsAllowed` / `DecisionsDenied` / `DecisionsUndetermined` | Berechtigungen, die Richtlinien erlaubt / verweigert / offen gelassen haben; offene werden im strikten Modus ebenfalls verweigert | +| `MissingSnapshots` | Callbacks, die ohne geprüften Richtlinien-Snapshot bedient wurden | +| `SnapshotVersion` | Version des verwendeten Richtlinien-Snapshots, 0 für keinen | +| `QueuedEvents` / `DroppedEvents` | Audit-Ereignisse, die im Speicher auf das Senden warten / die wegen voller Warteschlange oder vollem Plattenpuffer verworfen wurden | +| `ServerReachable` | Ob der letzte Zugriff auf den Richtlinienserver gelang (1/0) | ## Bereitstellung -1. Lege in GrantForge unter Datenberechtigungen → Datendienste einen `hdfs`-Dienst an, speichere die Konfiguration und teste die Verbindung; konfiguriere Pfadrichtlinien für die tatsächlichen Hadoop-Kurznamen, Benutzergruppen oder Rollen. -2. Stelle unter „Datenberechtigungen → Agenten“ für diesen Dienst ein Token aus. Schreibe das Token im Klartext in eine lokale Datei auf jedem NameNode, zum Beispiel `/etc/hadoop/grantforge/token`, lesbar für den Benutzer, unter dem der NameNode läuft. -3. Wähle die Hadoop-Linie des Clusters und kopiere `grantforge-agent-hdfs--.jar` aus `agents/hdfs//` auf den NameNode-Klassenpfad, zum Beispiel `$HADOOP_HOME/share/hadoop/hdfs/lib/`. Installiere dort genau einen passenden Adapter. Das Jar enthält die gemeinsame Agent-Logik, Jackson und die Signaturbibliothek; Hadoop kommt vom NameNode. -4. Konfiguriere die folgenden Eigenschaften in der `hdfs-site.xml` jedes NameNode; die beiden NameNodes eines HA-Setups verwenden unterschiedliche `instance`-Werte und jeweils eigene lokale Cache-Verzeichnisse. +1. Lege in den Datendiensten von GrantForge einen `hdfs`-Dienst an, speichere die Konfiguration und teste die Verbindung; richte Pfadrichtlinien für die tatsächlichen kurzen Hadoop-Benutzernamen, Gruppen oder Rollen ein. +2. Stelle unter „Datenberechtigungen → Agenten“ ein Token für diesen Dienst aus. Schreibe den Tokentext auf jedem NameNode in eine lokale Datei wie `/etc/hadoop/grantforge/token`, die der Benutzer des NameNode-Prozesses lesen kann. +3. Wähle nach der Tabelle das passende Agent-Jar aus `agents/hdfs//` des Release-Pakets und lege es in den Classpath des NameNodes, etwa `$HADOOP_HOME/share/hadoop/hdfs/lib/`. Das Agent-Jar enthält seine eigene Richtlinien-Engine, Jackson und die Signaturbibliothek; die Hadoop-Klassen stellt der NameNode bereit. Die Agent-Version im Heartbeat umfasst die Produktversion und die Hadoop-Version, gegen die kompiliert wurde. +4. Setze in der `hdfs-site.xml` jedes NameNodes die folgenden Eigenschaften; die beiden NameNodes eines HA-Paars verwenden unterschiedliche `instance`-Werte und je ein eigenes lokales Cache-Verzeichnis. ```xml @@ -102,58 +100,56 @@ Der Agent meldet Metriken über Hadoops Metrics2-System, über dieselben Sinks w ``` -5. Bestätige, dass `dfs.permissions.enabled=true` ist und dass `dfs.namenode.inode.attributes.provider.bypass.users` leer ist; beim Start lehnt der Agent Konfigurationen ab, die Autorisierungs-Callbacks umgehen könnten. Starte den NameNode neu und prüfe dann Heartbeat und Richtlinienversion auf der Agent-Seite von GrantForge. Der Agent liest die vorhandene Konfiguration des NameNode; er ändert die nativen Attribute von Inodes nicht. +5. Stelle sicher, dass `dfs.permissions.enabled=true` gilt und `dfs.namenode.inode.attributes.provider.bypass.users` leer ist; mit einer Konfiguration, die den Autorisierungs-Callback umgehen könnte, startet der Agent nicht. Starte den NameNode neu und prüfe Heartbeat und Richtlinienversion auf der Agentenseite von GrantForge. Der Agent liest die bestehende Konfiguration des NameNodes und ändert keine nativen Inode-Attribute. -Bei der ersten Bereitstellung kannst du zunächst mit `native.fallback=true` arbeiten, prüfen, ob das Richtlinien-Snapshot synchronisiert ist und die Berechtigungen auf den Vorfahrenverzeichnissen vollständig sind, und dann in den strikten Modus wechseln. Der Diensttyp, an den ein Token gebunden ist, muss `hdfs` lauten; eine Fehlkonfiguration oder eine Bindung an einen anderen Diensttyp führt dazu, dass Zugriffe abgelehnt werden. +Bei der ersten Bereitstellung kannst du mit `native.fallback=true` beginnen, prüfen, dass der Richtlinien-Snapshot synchronisiert ist, die Rechte auf Vorfahrenverzeichnisse ergänzen und dann in den strikten Modus wechseln. Das Token muss an den Diensttyp `hdfs` gebunden sein; eine falsche Konfiguration oder die Bindung an einen anderen Diensttyp führt zur Verweigerung. ## Optionale Einstellungen | Eigenschaft | Standardwert | Zweck | | --- | --- | --- | -| `grantforge.hdfs.connect.timeout.ms` | `5000` | Timeout für die Verbindung zu GrantForge | -| `grantforge.hdfs.read.timeout.ms` | `8000` | Timeout für das Lesen einer Antwort | -| `grantforge.hdfs.refresh.interval.ms` | `30000` | Intervall für die Richtlinienaktualisierung, solange der Server nicht erreichbar ist, mindestens `1000`; normale Heartbeats verwenden das vom Server vorgeschlagene Intervall | -| `grantforge.hdfs.signing.key.file` | nicht gesetzt | Optionale Datei mit dem öffentlichen Signaturschlüssel; Inhalt ist der von der Konsole bereitgestellte Base64-X.509-Schlüssel; nach der Konfiguration werden nur Signaturen dieses Schlüssels akzeptiert | -| `grantforge.hdfs.audit.batch.size` | `500` | Maximale Anzahl Ereignisse pro Meldung, Bereich `1..1000` | -| `grantforge.hdfs.audit.queue.capacity` | `10000` | Kapazität der Audit-Queue im Speicher, Bereich `1..1000000`, muss mindestens einen Batch fassen; wenn die Queue voll ist, werden neue Ereignisse gezählt und verworfen | -| `grantforge.hdfs.audit.flush.interval.ms` | `5000` | Audit-Flush-Intervall, positive Ganzzahl, höchstens `2147483647`; ein kleinerer Wert senkt die Meldelatenz | -| `grantforge.hdfs.audit.spool.limit.bytes` | `67108864` | Grenze des Plattenpuffers, solange der Server nicht erreichbar ist, nicht negative Ganzzahl; `0` deaktiviert den Plattenpuffer | +| `grantforge.hdfs.connect.timeout.ms` | `5000` | Timeout für den Verbindungsaufbau zu GrantForge | +| `grantforge.hdfs.read.timeout.ms` | `8000` | Timeout für das Lesen der Antwort | +| `grantforge.hdfs.refresh.interval.ms` | `30000` | Aktualisierungsintervall der Richtlinien, solange der Server nicht erreichbar ist, mindestens `1000`; der normale Heartbeat nutzt das vom Server empfohlene Intervall | +| `grantforge.hdfs.signing.key.file` | nicht gesetzt | Optionale Datei mit dem öffentlichen Signaturschlüssel, dem Base64-X.509-Schlüssel aus der Konsole; ist sie gesetzt, werden nur Signaturen mit diesem Schlüssel akzeptiert | +| `grantforge.hdfs.audit.batch.size` | `500` | Höchstzahl der Ereignisse pro Sendung, Bereich `1..1000` | +| `grantforge.hdfs.audit.queue.capacity` | `10000` | Kapazität der Audit-Warteschlange im Speicher, Bereich `1..1000000`, mindestens ein Batch; ist sie voll, werden neue Ereignisse gezählt und verworfen | +| `grantforge.hdfs.audit.flush.interval.ms` | `5000` | Intervall für das Leeren des Audits, positive Ganzzahl, höchstens `2147483647`; kleinere Werte senken die Verzögerung | +| `grantforge.hdfs.audit.spool.limit.bytes` | `67108864` | Obergrenze des Plattenpuffers, solange der Server nicht erreichbar ist, nicht negative Ganzzahl; `0` schaltet den Plattenpuffer ab | -Bei stark schwankendem Zugriffsaufkommen kannst du die Audit-Queue vergrößern, um Queue-Überläufe zu verringern; ein kürzeres Flush-Intervall senkt die Audit-Latenz, erhöht aber auch die Meldefrequenz. Die Grenze des Plattenpuffers begrenzt die Plattennutzung bei längeren Netzausfällen; wenn der Puffer deaktiviert oder erschöpft ist, können Ereignisse verloren gehen. Die Audit-Meldung läuft im Hintergrund und wartet nicht auf eine Antwort des Richtlinien-Servers. +Bei stark schwankendem Zugriffsaufkommen kannst du die Audit-Warteschlange vergrößern, damit sie seltener überläuft; ein kürzeres Intervall senkt die Audit-Verzögerung, erhöht aber die Sendehäufigkeit. Die Grenze des Plattenpuffers begrenzt den Plattenverbrauch bei langen Verbindungsausfällen; ist der Puffer abgeschaltet oder erschöpft, können Ereignisse verloren gehen. Das Audit wird im Hintergrund gesendet und wartet nicht auf den Richtlinienserver. -Ist kein öffentlicher Signaturschlüssel konfiguriert, holt ihn der Agent beim ersten Kontakt mit dem Server und speichert ihn zusammen mit dem Snapshot. Der Agent verwendet den von Hadoop übergebenen Kurznamen und die übergebenen Gruppen; Rollen und zusätzliche Gruppen kommen aus dem signierten Snapshot. Die Kurznamen-Abbildung für Kerberos-Principals wird durch `hadoop.security.auth_to_local` des Clusters bestimmt. +Ist kein öffentlicher Signaturschlüssel konfiguriert, holt ihn der Agent beim ersten Mal vom Server und speichert ihn mit dem Snapshot. Der Agent verwendet den kurzen Benutzernamen und die Gruppen, die Hadoop übergibt; Rollen und zusätzliche Gruppen stammen aus dem signierten Snapshot. Wie Kerberos-Principals auf kurze Namen abgebildet werden, bestimmt `hadoop.security.auth_to_local` des Clusters. ## Aus dem Quellcode bauen und verifizieren -Die Befehle zeigen 3.5; ersetze die Linie für den gewünschten Adapter durch 2.7, 2.10, 3.2, 3.3 oder 3.4. - ```sh ./mvnw -pl agents/grantforge-agent-hdfs-3.5 -am package ./mvnw -pl plugins/grantforge-plugin-hdfs,agents/grantforge-agent-hdfs-3.5,core/grantforge-plugin-host -am test ``` -Das Agent-Artefakt liegt unter `agents/grantforge-agent-hdfs-3.5/target/grantforge-agent-hdfs-3.5-.jar`. Unit-Tests decken die Autorisierungs-Callbacks des NameNode, die Konfiguration, die Versionsmetadaten und die Richtlinienentscheidungen ab; die WebHDFS- und Kerberos-Tests starten temporäre lokale Dienste. Vor der Inbetriebnahme solltest du auf dem Zielcluster zusätzlich Lesen/Schreiben, Erstellen, Umbenennen, rekursives Löschen, HA-Failover und das Cache-Verhalten nach einem Netzausfall verifizieren. +Das Agent-Artefakt liegt unter `agents/grantforge-agent-hdfs-3.5/target/grantforge-agent-hdfs-3.5-.jar`. Die Unit-Tests decken die Autorisierungs-Callbacks des NameNodes, die Konfiguration, die Versionsmetadaten und die Richtlinienentscheidungen ab; die WebHDFS- und Kerberos-Tests starten temporäre lokale Dienste. Vor dem Produktiveinsatz sind auf dem Zielcluster außerdem Lesen und Schreiben, Anlegen, Umbenennen, rekursives Löschen, HA-Umschaltung und das Cache-Verhalten nach einem Verbindungsausfall zu prüfen. -Die Integrationsverifikation läuft in der `verify`-Phase mit Testcontainers (Unit-Tests starten keinen Cluster; `verify` benötigt einen verfügbaren Docker-Daemon): +Die Integrationsverifikation läuft in der `verify`-Phase mit Testcontainers (Unit-Tests starten keinen Cluster; `verify` braucht einen verfügbaren Docker-Daemon): ```sh ./mvnw -pl agents/grantforge-agent-hdfs-3.5 -am verify -# Gleicher Einstieg wie bei nightly -bash script/ci/hdfs_integration.sh 3.5 -# Einzelne Linie oder die vollständige Matrix -bash script/ci/hdfs_integration.sh all +# derselbe Einstieg wie in nightly +bash script/ci/hdfs_integration.sh +# nur die angegebene Versionslinie prüfen +bash script/ci/hdfs_integration.sh 2.10 ``` -Die Tests verwenden Container-Images von Apache Hadoop mit fixierten Versionen und legen das tatsächlich paketierte Agent-Jar auf den Klassenpfad des NameNode. Testcontainers erzeugt ein isoliertes Netzwerk und verwaltet den Lebenszyklus von NameNode und DataNodes; verifiziert werden Lesen/Schreiben, Erstellen, Anhängen, Umbenennen, Löschen, rekursive und Snapshot-Ablehnung, native Berechtigungen und Audit, Richtlinienaktualisierung, der Neustart des NameNode gegen den signierten Cache nach Abbruch der Verbindung zum Richtlinien-Server sowie der strikte Modus mit nativen Berechtigungsrückfall, wenn kein Snapshot existiert. +Die Tests verwenden Apache-Hadoop-Images mit fester Version; ältere Images werden aus einem Java-8-Basisimage mit festem Digest und einer per SHA-512 geprüften Apache-Distribution gebaut. Das echte Agent-Jar der passenden Version liegt im Classpath des NameNodes, und die tatsächlichen Hadoop- und JVM-Versionen werden geprüft. Testcontainers legt ein isoliertes Netzwerk an und verwaltet den Lebenszyklus von NameNode und DataNode; geprüft werden Lesen und Schreiben, Anlegen, Anhängen, Umbenennen, Löschen, Verweigerungen bei Rekursion und Snapshots, native Berechtigungen und Audit, die Aktualisierung von Richtlinien, ein NameNode-Neustart mit signiertem Cache nach getrennter Verbindung zum Richtlinienserver sowie der strikte Modus und der Rückfall auf native Berechtigungen ohne Snapshot. -Ein laufender Docker-Daemon ist erforderlich, und das Herunterladen der Test-Images muss erlaubt sein. Steht Docker nicht zur Verfügung, schlagen die Tests fehl; sie werden nie stillschweigend übersprungen. Der Dateisystem-Client läuft innerhalb des Hadoop-Containers, und der HTTP-Dienst für Richtlinien verwendet die Host-Port-Weiterleitung von Testcontainers; ein externer Hadoop-Cluster ist nicht nötig. Nach den Tests werden Container und Test-Netzwerk aufgeräumt, und Logs werden unter `agents/grantforge-agent-hdfs-3.5/target/hdfs-testcontainers` gespeichert. +Ein laufender Docker-Daemon ist erforderlich, und das Herunterladen der Testimages muss erlaubt sein. Ist Docker nicht verfügbar, schlagen die Tests fehl, statt still übersprungen zu werden. Der Dateisystem-Client läuft im Hadoop-Container, und der HTTP-Dienst für Richtlinien wird über die Host-Portweiterleitung von Testcontainers erreicht; ein externer Hadoop-Cluster ist nicht nötig. Nach den Tests werden Container und Testnetzwerk entfernt, und die Logs liegen unter `agents/grantforge-agent-hdfs-3.5/target/hdfs-testcontainers`. -Der HA-Test startet zwei NameNodes, einen DataNode und einen JournalNode und konfiguriert für die beiden Agenten je einen eigenen Instanznamen und ein eigenes Cache-Verzeichnis. Er schaltet mit dem logischen HDFS-Client den aktiven Knoten manuell um und verifiziert Lese- und Schreibvorgänge sowie Ablehnen-Richtlinien nach der Umschaltung; der einzelne JournalNode dient nur dem Test, die Quorum-Fehlertoleranz wird nicht verifiziert, und auch das automatische Failover über ZooKeeper ist nicht Gegenstand des Tests. +Der HA-Test startet zwei NameNodes, einen DataNode und einen JournalNode und gibt den beiden Agenten eigene Instanznamen und Cache-Verzeichnisse. Er schaltet den aktiven Knoten mit einem logischen HDFS-Client von Hand um und prüft danach Lesen, Schreiben und verweigernde Richtlinien; ein einzelner JournalNode dient nur dem Test, sodass weder Mehrheitsfehlertoleranz noch automatisches Failover über ZooKeeper geprüft werden. -Der Test für automatisches HA (Hadoop 2.10 bis 3.5, Java 17 und 21) startet ZooKeeper, drei JournalNodes, zwei NameNodes mit je eigenem ZKFC und einen DataNode. Geprüft wird, dass nach dem Beenden des aktiven NameNode ZooKeeper den anderen übernehmen lässt, der die Richtlinien weiter durchsetzt und Ablehnungen unter seiner eigenen Instanz meldet; dass Richtlinien, die veröffentlicht werden, während ein NameNode fehlt, gelten, sobald er als Standby zurückkehrt, und auch nach dem Zurückwechseln gelten, nie eine ältere Version; dass der übernehmende NameNode ohne Richtlinienserver mit seinem vorhandenen Snapshot weiter durchsetzt und sein Audit nachliefert, sobald der Server zurück ist; und dass bei einem von drei gestoppten JournalNodes Schreibvorgänge gelingen, bei zwei gestoppten der Schreibvorgang scheitert und der aktive NameNode anhält, statt ohne Quorum weiterzumachen. Eine Kerberos-Variante, nur auf Hadoop 3.5.0, ergänzt JournalNodes, die sich per Keytab anmelden und nur HTTPS anbieten, ZKFCs, die sich per SASL bei ZooKeeper authentifizieren, und Wahl-Znodes, die nur der Principal der NameNodes nutzen darf (ein nicht authentifizierter ZooKeeper-Client kann sie nicht einmal lesen); geprüft wird, dass der andere NameNode übernimmt, wenn der aktive gestoppt wird, und für Kerberos-Benutzer weiter durchsetzt, dass ein Client ohne Ticket durchgehend abgewiesen wird und dass die Rolle zurückwechselt, sobald der erste NameNode wieder da ist. Die JournalNodes laufen unter einem Principal mit dem Kurznamen der NameNodes, weil ein JournalNode Edits nur an den vollständigen Principal eines NameNode oder an Anfragende mit seinem eigenen Kurznamen herausgibt und die HTTP-Authentifizierung ihm nur Kurznamen liefert. Automatisches HA auf Hadoop 2.7 ist nicht verifiziert: Dessen Client wechselt nur, solange sich der Hostname jedes NameNode auflösen lässt; der Name eines gestoppten Containers lässt sich nicht mehr auflösen, sodass er mit UnknownHostException aufgibt, statt den anderen NameNode zu versuchen. +Der Test für automatisches HA (Hadoop 2.10 bis 3.5, Java 17 und 21) startet ZooKeeper, drei JournalNodes, zwei NameNodes mit je einem ZKFC und einen DataNode. Er prüft: Wird der aktive NameNode direkt angehalten, wählt ZooKeeper einen anderen Knoten, der neue aktive Knoten setzt die Richtlinien weiter durch, und Verweigerungsereignisse gehören zu seiner Instanz; neue Richtlinien, die während der Abwesenheit eines Knotens veröffentlicht wurden, gelten nach seiner Rückkehr als Standby, und nach dem Zurückschalten gilt die neue Version statt der alten; ist der Richtliniendienst nicht erreichbar, setzt der übernehmende Knoten den bereits geladenen Snapshot weiter durch und sendet das Audit nach, sobald der Dienst wieder da ist; hält einer der drei JournalNodes an, gelingen Schreibvorgänge weiter, halten zwei an, schlagen sie fehl und der aktive NameNode beendet sich, statt ohne Mehrheit weiterzulaufen. Dazu gibt es eine Kerberos-Variante (nur Hadoop 3.5.0): Auch die JournalNodes melden sich per Keytab an und bieten nur HTTPS, die ZKFCs authentifizieren sich per SASL bei ZooKeeper, und auf den Wahlknoten darf nur der Principal des NameNodes zugreifen (ein nicht authentifizierter ZooKeeper-Client darf nicht einmal lesen); der Test prüft, dass nach dem Anhalten des aktiven NameNodes ein anderer Knoten übernimmt und Richtlinien für Kerberos-Benutzer weiter durchsetzt, dass Clients ohne Ticket stets abgewiesen werden und dass nach der Rückkehr des ursprünglichen Knotens zurückgeschaltet werden kann. Die JournalNodes laufen mit einem Principal, der denselben kurzen Namen hat wie der des NameNodes, denn ein JournalNode gibt Edits nur an den vollständigen Principal des NameNodes oder an Anfragende mit seinem eigenen kurzen Namen heraus, und die HTTP-Authentifizierung liefert nur den kurzen Namen. Automatisches HA mit Hadoop 2.7 ist nicht geprüft: Dessen Client schaltet nur um, wenn sich alle NameNode-Hostnamen auflösen lassen; der Name des im Test angehaltenen Containers lässt sich nicht mehr auflösen, sodass der Client sofort eine UnknownHostException meldet, ohne den anderen Knoten zu versuchen. -Der Kerberos-Test läuft auf allen sechs Versionslinien (Java 17 und 21): Das KDC läuft in der Test-JVM, NameNode und DataNode starten im sicheren Modus mit eigenen Keytabs, der DataNode überträgt Daten nur nach SASL und bietet nur HTTPS, WebHDFS nutzt SPNEGO. Geprüft wird, dass GrantForge-Richtlinien für Lesen und Schreiben gelten, sobald Principals auf Kurznamen abgebildet sind, dass ein Benutzer ohne Richtlinie abgewiesen wird, dass Ablehnungen unter dem Kurznamen protokolliert werden, dass ein Client ohne Ticket abgewiesen wird, ohne auf Simple-Authentifizierung zurückzufallen, und dass ein neu gestarteter NameNode sich erneut anmeldet und weiter durchsetzt. +Der Kerberos-Test läuft auf allen sechs Versionslinien (Java 17 und 21): Das KDC läuft in der Test-JVM, NameNode und DataNode starten im sicheren Modus mit je eigenem Keytab, die Datenübertragung des DataNodes verlangt SASL und bietet nur HTTPS, und WebHDFS verwendet SPNEGO. Geprüft wird, dass GrantForge-Richtlinien nach der Abbildung des Principals auf den kurzen Namen für Lesen und Schreiben gelten, dass Benutzer ohne Richtlinie abgewiesen werden, dass Verweigerungen unter dem kurzen Namen protokolliert werden, dass Clients ohne Ticket abgewiesen werden und nicht auf Simple-Authentifizierung zurückfallen und dass sich der NameNode nach einem Neustart erneut anmeldet und die Richtlinien weiter durchsetzt. -Gemeinsame native Unit-Tests laufen in `agents/grantforge-agent-hdfs-native/src/test`, nicht mehr in allen sechs Adaptern. Versionsabhängige Callback-Tests bleiben in ihren nummerierten Modulen. Die gemeinsamen Containertest-Quellen in `agents/grantforge-agent-hdfs-common/src/test/shared` werden weiterhin in die nummerierten Produktionsmodule kompiliert. Es gibt kein eigenes Maven-Testprojekt; Testcontainers bleibt eine Testabhängigkeit. Nightly prüft alle sechs Hadoop-Versionen mit Java-17/21-Testhosts; die Java-Version innerhalb der Container folgt der obigen Matrix. Berichte und Container-Logs werden gespeichert. +Die Testquellen liegen in `src/test` der eigentlichen Produktionsmodule. Gemeinsame Unit-Tests der Richtlinien laufen in common, gemeinsame native Unit-Tests in `agents/grantforge-agent-hdfs-native/src/test`; sie werden nicht mehr in die sechs Versions-Agenten kompiliert. Die Callback-Tests jeder Version bleiben im jeweiligen nummerierten Modul, und die gemeinsamen Container-Testquellen in `agents/grantforge-agent-hdfs-common/src/test/shared` werden weiterhin in jedes nummerierte Modul kompiliert; ein eigenes Maven-Testprojekt gibt es nicht. Testcontainers ist nur im Scope test eingebunden. Nightly prüft die sechs Hadoop-Versionen auf Hosts mit Java 17 und 21, die Hadoop-Container verwenden die JVMs aus der Tabelle. Derzeit decken die Container Simple-Authentifizierung, manuelles HA, Kerberos und HTTPS auf allen sechs Versionslinien, automatisches HA für 2.10 bis 3.5 und automatisches HA unter Kerberos auf Hadoop 3.5.0 ab; automatisches HA mit 2.7, automatisches HA unter Kerberos auf anderen Versionen und Hersteller-Patches müssen weiterhin in der Zielumgebung geprüft werden. -Zu den Hadoop-Erweiterungspunkten und der Berechtigungssemantik siehe [Apache Hadoop 3.5.0 API](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/build/source/hadoop-hdfs-project/hadoop-hdfs/target/api/org/apache/hadoop/hdfs/server/namenode/INodeAttributeProvider.html) und [HDFS-Berechtigungsleitfaden](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/HdfsPermissionsGuide.html). +Erweiterungspunkte und Berechtigungssemantik von Hadoop beschreiben die [Apache Hadoop 3.5.0 API](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/build/source/hadoop-hdfs-project/hadoop-hdfs/target/api/org/apache/hadoop/hdfs/server/namenode/INodeAttributeProvider.html) und der [HDFS-Berechtigungsleitfaden](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/HdfsPermissionsGuide.html). diff --git a/docs/content/de/guide/catalog.md b/docs/content/de/guide/catalog.md index f3f51cf2..940aef46 100644 --- a/docs/content/de/guide/catalog.md +++ b/docs/content/de/guide/catalog.md @@ -22,6 +22,12 @@ description: Anwendungen und Ressourcenbäume, Ressourcenabhängigkeiten und OAu - **OAuth-Clients**: Registriere Clients für Geschäftsanwendungen, siehe [OAuth 2.1 und OpenID Connect](/de/integration/oauth/). - **Felder**: Wenn ein Feld ausgewählt ist, wird angezeigt, in welchen Schnittstellen es vorkommt (zurückgegeben oder empfangen). +Klicke im Katalog einer Anwendung mit Ressourcen auf **Exportieren**, um eine JSON-Datei für Entwicklung oder KI-Werkzeuge herunterzuladen. Sie enthält die Anwendungsdaten, alle Ressourcen nach Hierarchie geordnet und ihre Abhängigkeiten samt den Ressourcencodes an beiden Enden; Suche und Filter ändern den Export nicht. `formatVersion` gibt die Version des Exportformats an. + +Wähle die Zielanwendung, klicke auf **Importieren**, lade eine Exportdatei der Version 1 hoch und lass sie zuerst prüfen. Der Import verwendet Ressourcen mit gleichem Code in derselben Anwendung nur wieder, wenn Typ und übergeordnete Ressource übereinstimmen, und überschreibt keine bestehenden Einstellungen; ein Konflikt bei Typ, übergeordneter Ressource oder Abhängigkeitsart verwirft den ganzen Import. Neue Ressourcen und Abhängigkeiten werden nach der Bestätigung gemeinsam geschrieben, und ein wiederholter Import legt keine Duplikate an. Eingebaute Anwendungen unterstützen keinen Import; deklarierte Abhängigkeiten werden nach dem Import zu manuellen. Für den Import von Ressourcen ist das Recht zum Anlegen von Ressourcen nötig, für Abhängigkeiten zusätzlich das Recht zum Ändern. + +Der Export enthält nur, was in GrantForge erfasst ist: `route` bei Seiten und ähnlichen Ressourcen ist die Frontend-Route, `code` bei API-Ressourcen der Berechtigungscode. HTTP-Methoden und Pfade externer Geschäfts-APIs werden nicht abgeleitet, wenn sie nicht erfasst sind. Das JSON ist ein Schnappschuss zum Zeitpunkt des Exports ohne Berechtigungen irgendeines Benutzers; ändern sich die Ressourcen, exportiere erneut. + ## API-Katalog **Plattformverwaltung → API-Katalog** listet alle Schnittstellen, die beim Start des Servers automatisch registriert werden, mit ihrer Zugriffsvoraussetzung: öffentlich, jeder angemeldete Benutzer oder ein bestimmter Berechtigungscode. Schnittstellen, die eine Autorisierung erfordern, werden nach Berechtigungscode in den Ressourcenkatalog eingeordnet, und Rollenautorisierungen verweisen auf diese Berechtigungen. Wird eine Schnittstelle hinzugefügt, außer Betrieb genommen oder ändert sich ihr Berechtigungscode, wird sie als „ausstehende Änderung“ geführt; nach der Bestätigung wird der Katalog aktualisiert. diff --git a/docs/content/de/guide/explain.md b/docs/content/de/guide/explain.md index 9830a2a2..fdc912b7 100644 --- a/docs/content/de/guide/explain.md +++ b/docs/content/de/guide/explain.md @@ -13,6 +13,8 @@ description: Ansehen, was eine Person darf und warum, Rollenänderungen simulier Klicke in der **Benutzerverwaltung** in der Zeile des Benutzers auf „Effektive Berechtigungen ansehen“, um alles zu sehen, was der Benutzer aktuell nutzen kann: Rollen, Menüs und Schaltflächen, APIs, Datenbereiche und eingeschränkte Felder. +Wähle eine Anwendung, um ihre effektiven Ressourcen und API-Berechtigungen zu sehen; „Warum“ zeigt, ob sie aus Rollen oder direkten Berechtigungen des Benutzers stammen. Rollen, Datenbereiche und Feldberechtigungen zeigen die Einstellungen des ganzen Kontos. Beim Simulieren hinzugefügter oder entfernter Rollen bleiben bestehende direkte Berechtigungen erhalten. + ![Effektive Berechtigungen](/screenshots/user-permissions.png) Klicke bei einem beliebigen Eintrag auf **Erklärung**, dann beantwortet GrantForge, „warum es funktioniert“: welche Rolle es gewährt, über welche Zuweisungen (direkt, Gruppe, Abteilung, Stelle) und welche Herleitungen aus Ressourcen es zustande kommt. Funktioniert etwas nicht, steht dort, ob es nie gewährt wurde oder von einer Verweigerungsregel blockiert ist. diff --git a/docs/content/de/guide/identity-sources.md b/docs/content/de/guide/identity-sources.md index 2dee51a0..6752ebe5 100644 --- a/docs/content/de/guide/identity-sources.md +++ b/docs/content/de/guide/identity-sources.md @@ -44,6 +44,11 @@ Wähle als Typ „OpenID Connect“, um Keycloak, Azure AD, Okta, ein weiteres G | Client-ID / Client-Secret | der beim Provider registrierte Client; ohne Secret als öffentlicher Client mit PKCE | | Callback-Adresse | die auf der Seite angezeigte Adresse `/api/v1/auth/federated/callback/`; sie muss im Client beim Provider eingetragen werden | | Scopes und Claims | standardmäßig `openid profile email`; Benutzername, Anzeigename und E-Mail-Adresse werden aus den Claims `preferred_username`, `name` bzw. `email` gelesen | +| Telefon-Claim | standardmäßig `phone` (Casdoor); bei anderen Providern lässt sich `phone_number` eintragen. Damit Telefonnummern synchronisiert werden, muss der Scope `phone` enthalten. Liefert der Provider bei der Anmeldung keine Nummer, bleibt die vorhandene erhalten | +| Avatar-Adresse | wird aus dem OIDC-Standard-Claim `picture` übernommen; der Scope muss `profile` enthalten. Fehlt der Claim, bleibt die vorhandene Avatar-Adresse erhalten | +| Zusätzliche Parameter der Autorisierungsanfrage | ein optionales JSON-Objekt, dessen Werte Zeichenketten sein müssen. Bei Casdoor wählt etwa `{"provider_hint":"github"}` einen konfigurierten Provider. Die Parameter stehen in der Adresszeile des Browsers; trage dort keine Geheimnisse ein | + +Die tragenden Protokollparameter (darunter `state`, `nonce`, `redirect_uri` und die PKCE-Parameter) verwaltet GrantForge; zusätzliche Parameter können sie nicht überschreiben. Nach dem Aktivieren erscheint auf der Anmeldeseite die Schaltfläche „Mit anmelden“. Meldet sich der Benutzer beim Provider an, kehrt er zu GrantForge zurück; Konten mit aktiver Zwei-Faktor-Authentifizierung müssen zusätzlich einen Code eingeben. @@ -54,3 +59,26 @@ Nach dem Aktivieren erscheint auf der Anmeldeseite die Schaltfläche „Mit Löschen - ``` + +``` `useGrantForge()` stellt `authorization`, `error`, `refresh()`, `can()` und `hasResource()` bereit. Das Ausblenden von Schaltflächen ist nur ein Komfort; die APIs der Anwendung müssen die Berechtigungen genauso prüfen. diff --git a/docs/content/de/start/quick-start.md b/docs/content/de/start/quick-start.md index 2431fa7b..7ab1e063 100644 --- a/docs/content/de/start/quick-start.md +++ b/docs/content/de/start/quick-start.md @@ -38,7 +38,7 @@ Die Logdatei des Release-Pakets liegt unter `logs/grantforge.log`; bei Docker si ## 2. Initialisierung abschließen -Öffne im Browser http://127.0.0.1:9999/; die Konsole wechselt automatisch zur Initialisierungsseite. Trage das Token aus der Logdatei, den Namen der Organisation sowie Benutzernamen und Passwort des ersten Administrators ein (mindestens 12 Zeichen). +Öffne im Browser ; die Konsole wechselt automatisch zur Initialisierungsseite. Trage das Token aus der Logdatei, den Namen der Organisation sowie Benutzernamen und Passwort des ersten Administrators ein (mindestens 12 Zeichen). ![Erstinitialisierung und Anmeldeseite](/screenshots/login.png) diff --git a/docs/content/en/architecture/overview.md b/docs/content/en/architecture/overview.md index 5121207a..d7fbca5e 100644 --- a/docs/content/en/architecture/overview.md +++ b/docs/content/en/architecture/overview.md @@ -59,19 +59,19 @@ The diagram above shows the dependencies between modules (lower layers such as c ```mermaid sequenceDiagram - participant B as 控制台 - participant F as 过滤链 + participant B as Console + participant F as Filter chain participant G as PermissionGuard - participant C as 控制器 - participant S as 领域服务 - B->>F: 会话 Cookie + X-XSRF-TOKEN - F->>F: 会话、CSRF、绑定租户、记录活动 - F->>G: 已登录 - G->>G: 读取账号的权限快照,检查接口声明的权限码 - G->>C: 通过(响应带 X-Authorization-Version) - C->>S: 调用服务,带上操作者 - S->>S: 数据范围、字段规则、业务校验 - S-->>B: JSON 或 RFC 9457 problem details + participant C as Controller + participant S as Domain service + B->>F: Session cookie + X-XSRF-TOKEN + F->>F: Session, CSRF, bind the tenant, record activity + F->>G: Signed in + G->>G: Read the account's permission snapshot, check the permission code the endpoint declares + G->>C: Allowed (the response carries X-Authorization-Version) + C->>S: Call the service with the acting user + S->>S: Data scopes, field rules, business validation + S-->>B: JSON or RFC 9457 problem details ``` - Every controller method must declare its access mode (public, any signed-in account, or a required permission code); a method without a declaration prevents the server from starting. diff --git a/docs/content/en/architecture/performance.md b/docs/content/en/architecture/performance.md index 6015b27d..33de9629 100644 --- a/docs/content/en/architecture/performance.md +++ b/docs/content/en/architecture/performance.md @@ -42,9 +42,9 @@ Each metric is warmed up and then measured call by call. ## Running it locally ```bash -bash script/ci/perf_benchmark.sh full # 目标规模,postgres:17(需要 Docker),检查上限 -bash script/ci/perf_benchmark.sh smoke # H2 上的小规模,只确认基准能运行 -bash script/ci/perf_benchmark.sh full mysql:8.4 # 其他数据库 +bash script/ci/perf_benchmark.sh full # target scale on postgres:17 (needs Docker), checks the limits +bash script/ci/perf_benchmark.sh smoke # small scale on H2, only checks that the benchmark runs +bash script/ci/perf_benchmark.sh full mysql:8.4 # another database ``` The report is written to `perf/target/perf-report.json`, and a table is also printed to the log. Extra options can be passed with `PERF_OPTS`: diff --git a/docs/content/en/changelog/2026.4.0.md b/docs/content/en/changelog/2026.4.0.md new file mode 100644 index 00000000..8a46fa6e --- /dev/null +++ b/docs/content/en/changelog/2026.4.0.md @@ -0,0 +1,68 @@ +--- +title: "2026.4.0" +description: "GrantForge 2026.4.0 (2026-11-08): service plug-ins and agents for Elasticsearch, Apache Doris, MongoDB and Redis/Valkey, DingTalk organization sync, direct user grants, resource catalog export and import, and CI that checks every language's translations are complete." +--- + + +2026.4.0 extends the external system permissions to Elasticsearch, Apache Doris, MongoDB, and Redis and Valkey. All four agents run beside the system and keep its own access control in step with the policies. On the identity and authorization side, it adds DingTalk organization sync, direct user grants, and export and import of the resource catalog. See the [GitHub comparison](https://github.com/devlive-community/grantforge/compare/v2026.3.0...v2026.4.0) for every change. + +## Elasticsearch + +- Elasticsearch service plug-in: indices and data streams, their fields, and the cluster; signs in with a password, an API key or a TLS client certificate; see [Elasticsearch](/en/plugins/elasticsearch/). +- Agent beside the cluster: keeps Elasticsearch's own role for every user, assigned to native users and through role mappings to users of other realms, with field- and document-level security where the licence allows it; index patterns stay patterns, so new indices are reachable at once; see [Elasticsearch agent](/en/external/elasticsearch-agent/). +- Accesses come from the audit log files. Integration tests cover Elasticsearch 8.19, 9.3, 9.4 and 9.5. + +## Apache Doris + +- Doris service plug-in: catalogs, databases, tables, columns and workload groups; connects to one or more FEs and signs in with a password; see [Apache Doris](/en/plugins/doris/). +- Agent beside the cluster: policies become Doris's own roles, column grants, workload group grants and row policies, granted to every identity of the user; a whole database or catalog is granted only when Doris would not give more by it; see [Apache Doris agent](/en/external/doris-agent/). +- Accesses come from Doris's audit log table. Integration tests cover Doris 2.1, 3.1, 4.0 and 4.1. + +## MongoDB + +- MongoDB service plug-in: databases and collections; connects to a replica set or a sharded cluster and signs in with SCRAM, a client certificate, LDAP or Kerberos; see [MongoDB](/en/plugins/mongodb/). +- Agent beside the deployment: keeps one MongoDB role for every user and replaces its privileges in one step; from the inherited privileges MongoDB reports, it tells what built-in and custom roles outside GrantForge actually grant; see [MongoDB agent](/en/external/mongodb-agent/). +- Accesses come from the Enterprise or Percona audit log files, or from the profiler. Integration tests cover MongoDB 6.0, 7.0, 8.0 and 9.0, with Kerberos verified on Percona Server for MongoDB. + +## Redis and Valkey + +- Redis service plug-in: keys, channels and nodes; connects to a standalone server with its replicas or to a cluster, and signs in as an ACL user, optionally over TLS; see [Redis](/en/plugins/redis/). +- Agent beside the deployment: keeps every user's ACL selectors in step with the policies on every node and replaces them with one command; root permissions stay with the operators, and the agent asks Redis with `ACL DRYRUN` whether they allow access the policies deny; see [Redis agent](/en/external/redis-agent/). +- Refusals come from each node's ACL log. Integration tests cover Redis 7.2, 7.4, 8.2 and 8.10 and Valkey 8.1 and 9.1, as well as a Redis 8.10 cluster. + +## Agents + +- The ClickHouse, PostgreSQL and OpenSearch agents and this release's new agents beside a system share the new `grantforge-agent-sync` core: launcher, settings, timers, sync loop, audit read positions and log file tailing. Each agent keeps only what belongs to its system; settings and log formats are unchanged. + +## Identity and organization + +- Data sources: organization sync is configured apart from identity sources, and the first adapter is DingTalk, using its official server SDK; after an SSO sign-in, users are linked to their departments and primary department by their trusted email address; the tables migrate on all six supported databases, SQL Server and Oracle included; see [Identity sources (LDAP and OIDC)](/en/guide/identity-sources/). +- OpenID Connect identity sources can sync the phone number and the avatar and can add parameters to the authorization request (such as Casdoor's `provider_hint`); the protocol's own parameters cannot be overridden. + +## Authorization and resource catalog + +- Direct user grants: allow menus, pages, buttons or APIs of a business application for a single user, optionally until an expiry time; they combine with role permissions, and a role's denial still wins; see [Users](/en/guide/users/). +- The resource catalog can export an application's resource tree as JSON and import it into another application: the import is previewed first and nothing applies when it conflicts; platform administrators can clear eligible application trees; see [Resource and API catalog](/en/guide/catalog/). + +## Server + +- At startup the server prints a GrantForge banner with its version and the Java version instead of the Spring Boot banner. + +## Documentation and release + +- The plug-ins tab opens with an overview, groups the systems by kind in a folding sidebar, and lists each system's enforcement, row filter and masking support and verified versions; every README lists the components in the same order. +- CI checks that every page is translated completely into every language: the same paragraphs, headings, tables, code blocks and links as the Chinese page, no text in another language's script, and nothing copied from the English page; the 11 READMEs get the same checks against the English README. Every missing translation the check found has been filled in. +- The version check keeps the version badge of every README current. +- The publish docs and release workflows no longer run on forks. +- The server `grantforge-server` is no longer published to Maven repositories: it bundles every plug-in and exceeds their size limit; it ships in the release archive and the image. + +## Fixes + +- Changing a resource's type in the resource editor clears the resource code that no longer applies. +- The last code block of the JavaScript integration guide no longer swallows the text after it, and the console address on the Docker and quick start pages no longer turns the following text into a link. +- Dependencies upgraded to versions that fix security advisories: MariaDB Connector/J 3.5.11, OpenTelemetry 1.67.0, Guava 33.7.2, Snappy 1.1.10.9, and vue-i18n, glob and minimatch in the console and the SDK. diff --git a/docs/content/en/deploy/databases.md b/docs/content/en/deploy/databases.md index 813820f6..21542b3a 100644 --- a/docs/content/en/deploy/databases.md +++ b/docs/content/en/deploy/databases.md @@ -31,7 +31,7 @@ GRANTFORGE_DB_URL=jdbc:postgresql://db:5432/grantforge GRANTFORGE_DB_URL=jdbc:mysql://db:3306/grantforge # MariaDB GRANTFORGE_DB_URL=jdbc:mariadb://db:3306/grantforge -# Oracle(服务名) +# Oracle (service name) GRANTFORGE_DB_URL=jdbc:oracle:thin:@//db:1521/FREEPDB1 # SQL Server GRANTFORGE_DB_URL=jdbc:sqlserver://db:1433;databaseName=grantforge;encrypt=true;trustServerCertificate=true diff --git a/docs/content/en/deploy/docker.md b/docs/content/en/deploy/docker.md index e18886d9..bade386c 100644 --- a/docs/content/en/deploy/docker.md +++ b/docs/content/en/deploy/docker.md @@ -11,7 +11,7 @@ description: Run GrantForge in a container image, try it against various databas ## Image -Every release publishes the image `ghcr.io/devlive-community/grantforge:<版本>` (linux/amd64 and linux/arm64); stable releases also update `latest`: +Every release publishes the image `ghcr.io/devlive-community/grantforge:` (linux/amd64 and linux/arm64); stable releases also update `latest`: ```bash docker run -p 9999:9999 ghcr.io/devlive-community/grantforge:2026.0.0 @@ -43,7 +43,7 @@ docker compose -f deploy/compose/postgres.yml up -d docker compose -f deploy/compose/postgres.yml logs grantforge | grep "setup token" ``` -Then open http://127.0.0.1:9999/. The default database password used in the examples is for evaluation only; change it via `GRANTFORGE_DB_PASSWORD` before real use. The MySQL example requires putting `mysql-connector-j-<版本>.jar` into `deploy/compose/drivers/` first. +Then open . The default database password used in the examples is for evaluation only; change it via `GRANTFORGE_DB_PASSWORD` before real use. The MySQL example requires putting `mysql-connector-j-.jar` into `deploy/compose/drivers/` first. ## Helm diff --git a/docs/content/en/deploy/installation.md b/docs/content/en/deploy/installation.md index 064de042..af1eb414 100644 --- a/docs/content/en/deploy/installation.md +++ b/docs/content/en/deploy/installation.md @@ -36,10 +36,10 @@ Extracting `grantforge-release.tar.gz` yields a `grantforge/` directory: ## Start and Stop ```bash -bin/startup.sh # 后台启动,进程号写入 pid 文件 -bin/shutdown.sh # 按 pid 文件优雅停止 -bin/restart.sh # 停止后再启动 -bin/debug.sh # 前台运行,日志同时输出到控制台,Ctrl+C 停止 +bin/startup.sh # start in the background, writing the process id to the pid file +bin/shutdown.sh # stop gracefully using the pid file +bin/restart.sh # stop, then start again +bin/debug.sh # run in the foreground with the log on the console too; Ctrl+C stops it ``` The scripts can be run from any directory; the installation directory is the parent of the directory containing the script, or you can set it explicitly with the `GRANTFORGE_HOME` environment variable. diff --git a/docs/content/en/deploy/upgrade.md b/docs/content/en/deploy/upgrade.md index 9276ef2c..47675f7e 100644 --- a/docs/content/en/deploy/upgrade.md +++ b/docs/content/en/deploy/upgrade.md @@ -23,9 +23,9 @@ When upgrading a cluster, stop all instances before starting the new version so 1.x stores its data in a different set of tables, and 2.x does not read them. The migration path is: install 2.x on a **new database** and complete initialization, stop the service, then import the old database's accounts, roles, and menus into a tenant: ```bash -# 先预演:只生成报告,不写入 +# rehearse first: write the report only, change nothing bin/import-legacy.sh --source-url jdbc:mysql://old-db:3306/authx --source-user authx --tenant default -# 确认报告后正式导入 +# import for real once the report is confirmed bin/import-legacy.sh --source-url jdbc:mysql://old-db:3306/authx --source-user authx --tenant default --apply ``` @@ -35,7 +35,7 @@ Import rules: - **Accounts** keep their original passwords, which are automatically re-hashed with the new algorithm on first login. Accounts that violate the 2.x username rules (3–64 letters, digits, or `._@-`), have no password, or whose username is already taken by another tenant are skipped. - **Roles** keep their names; codes are lowercased (`GLY` → `gly`). -- **Menus** become resources of the `legacy` application: menus with `#` addresses become groupings, other addresses become pages, and menus under a page become buttons. A menu address and its HTTP method become an API resource `api:<方法>:<路径>`; addresses ending in `*` become `<路径>/**`, matched by path segments instead of character prefixes — the report lists each one for review. +- **Menus** become resources of the `legacy` application: menus with `#` addresses become groupings, other addresses become pages, and menus under a page become buttons. A menu address and its HTTP method become an API resource `api::`; addresses ending in `*` become `/**`, matched by path segments instead of character prefixes — the report lists each one for review. - Only **explicit grants** are migrated: 1.x let anyone access addresses registered as menus, and 2.x does not. > [!WARNING] diff --git a/docs/content/en/develop/development.md b/docs/content/en/develop/development.md index d22c89ce..10aa81fc 100644 --- a/docs/content/en/develop/development.md +++ b/docs/content/en/develop/development.md @@ -19,13 +19,13 @@ description: Local builds, tests, coding standards, and CI checks. ## Common commands ```bash -./mvnw verify # 构建并测试全部 Java 模块(含控制台) -./mvnw verify -DskipFrontend # 跳过控制台构建 -bash script/ci/web.sh test # 控制台单元测试 -bash script/ci/web.sh e2e # 控制台浏览器测试(模拟后端) -bash script/ci/e2e_fullstack.sh # 打包、启动真实服务并运行全栈测试 -bash script/ci/db_integration.sh postgres:17 # 在指定数据库上运行集成测试 -bash script/ci/perf_benchmark.sh smoke # 小规模性能基准 +./mvnw verify # build and test every Java module (console included) +./mvnw verify -DskipFrontend # skip the console build +bash script/ci/web.sh test # console unit tests +bash script/ci/web.sh e2e # console browser tests (mocked back end) +bash script/ci/e2e_fullstack.sh # package, start the real server and run the full-stack tests +bash script/ci/db_integration.sh postgres:17 # integration tests on the given database +bash script/ci/perf_benchmark.sh smoke # small performance benchmark ``` For console development, run `pnpm dev` in `core/grantforge-web`; Vite proxies `/api` and other requests to the server at `localhost:9999`. @@ -68,15 +68,15 @@ Versions are `year.minor.patch` (for example `2026.0.0`), with release candidate Release from the `dev` branch with a single command: ```bash -bash script/release/tag.sh 2026.1.0 --dry-run # 只检查并预览发布说明,不做任何修改 -bash script/release/tag.sh 2026.1.0 --next 2026.2.0 # 设置版本、打标签 v2026.1.0 并推送,再把 dev 切到下一个版本 +bash script/release/tag.sh 2026.1.0 --dry-run # check and preview the release notes only, change nothing +bash script/release/tag.sh 2026.1.0 --next 2026.2.0 # set the version, tag v2026.1.0 and push, then move dev to the next version ``` The script requires a clean working tree, a local branch not behind the remote, and a nonexistent tag; after confirmation it commits `chore(release): prepare `, creates an annotated tag, and pushes. The tag triggers `release.yml`: - `script/ci/release.sh` builds the distribution package, a CycloneDX SBOM containing only release dependencies, and `SHA256SUMS`; - Multi-architecture images are pushed to `ghcr.io/devlive-community/grantforge`; -- Maven artifacts (including sources and Javadoc) are published to GitHub Packages; when the repository is configured with `CENTRAL_USERNAME`, `CENTRAL_PASSWORD` (a Central Portal token), `GPG_PRIVATE_KEY`, and `GPG_PASSPHRASE`, they are signed and published to Maven Central; +- Maven artifacts (including sources and Javadoc) are published to GitHub Packages; when the repository is configured with `CENTRAL_USERNAME`, `CENTRAL_PASSWORD` (a Central Portal token), `GPG_PRIVATE_KEY`, and `GPG_PASSPHRASE`, they are signed and published to Maven Central; the server `grantforge-server` bundles every plug-in and exceeds the repositories' size limit, so it goes to no Maven repository and ships only in the release archive and the image; - A GitHub Release is created whose body lists all commits since the previous release (`v*` or a numeric tag such as `1.0.6`), grouped into features, fixes, performance, and so on, with links to the commits. Release candidates are marked as pre-release and do not update the images' `latest` tag. Locally, the `central` profile does not publish by default (`central.skip=true`); only the release workflow explicitly passes `-Dcentral.skip=false`. @@ -95,8 +95,10 @@ This site lives in `docs/` and uses Next.js static export with Tailwind CSS: cd docs pnpm install pnpm dev # http://localhost:3100 -pnpm check # 页面、链接与图片检查 -pnpm build # 输出到 docs/out +pnpm check # page, link and image checks +pnpm build # output to docs/out ``` Pages are Markdown under `docs/content/`, and navigation is defined in `docs/lib/navigation.ts`. The API reference and error codes are generated at build time from the contract and the source code. Screenshots are produced by `script/docs/screenshots.sh`, which starts the real server, seeds sample data, and drives Playwright. + +`pnpm test` also checks the translations: every Chinese page needs a translation in each of the other ten languages, with the same paragraphs, heading levels, lists, tables, code blocks and links as the Chinese page. A page must not contain text in a script its language does not use (including simplified characters in Traditional Chinese and Japanese), nor sentences copied unchanged from the English page. The 11 READMEs at the repository root get the same checks against the English README, and their links to this site must point at existing pages in their own language. diff --git a/docs/content/en/develop/plugins.md b/docs/content/en/develop/plugins.md index 84869acf..da496306 100644 --- a/docs/content/en/develop/plugins.md +++ b/docs/content/en/develop/plugins.md @@ -59,7 +59,7 @@ public final class ExampleProvider implements ServiceTypeProvider @Override public List lookup(LookupRequest request) { - // 返回 request.resource() 层级下以 request.userInput() 开头的候选值,最多 request.limit() 个 + // Return candidates below request.resource() that start with request.userInput(), at most request.limit() return List.of(); } } @@ -185,7 +185,7 @@ AgentSettings settings = AgentSettings.builder() .instance("namenode-1:8020") .cacheDirectory(Paths.get("/var/lib/grantforge-agent")) .build(); -GrantForgeAgent agent = GrantForgeAgent.start(settings, evaluators); // 条件求值器,按名称 +GrantForgeAgent agent = GrantForgeAgent.start(settings, evaluators); // condition evaluators, by name AgentDecision decision = agent.decide(AccessRequest.builder(user, "read").groups(groups).resource("path", path).build()); agent.record(AccessEvent.builder(user, path, "read", allowed).decidedBy(decision).action("open").build()); diff --git a/docs/content/en/guide/identity-sources.md b/docs/content/en/guide/identity-sources.md index 01814f5b..7932055c 100644 --- a/docs/content/en/guide/identity-sources.md +++ b/docs/content/en/guide/identity-sources.md @@ -42,8 +42,13 @@ Choose the type "OpenID Connect" to connect Keycloak, Azure AD, Okta, another Gr | --- | --- | | Issuer URL | The provider's issuer address; GrantForge discovers endpoints and keys from its discovery document | | Client ID / secret | The client registered with the provider; without a secret it acts as a public client using PKCE | -| Redirect URI | The `/api/v1/auth/federated/callback/<编码>` shown on the page; it must be registered on the provider's client | +| Redirect URI | The `/api/v1/auth/federated/callback/` shown on the page; it must be registered on the provider's client | | Scopes and claims | Defaults to `openid profile email`; username, display name, and email are taken from the `preferred_username`, `name`, and `email` claims respectively | +| Phone claim | Reads `phone` by default (Casdoor); other providers can use `phone_number`. To sync phone numbers, the scope must include `phone`. When the provider returns no number at sign-in, the existing number is kept | +| Avatar URL | Synchronized from the standard OIDC `picture` claim; the scope must include `profile`. When none is returned, the existing avatar URL is kept | +| Extra authorization parameters | An optional JSON object whose values must be strings. Casdoor, for example, accepts `{"provider_hint":"github"}` to pick a configured provider. The parameters appear in the browser's address bar, so never put secrets in them | + +GrantForge manages the protocol's own parameters (including `state`, `nonce`, `redirect_uri` and the PKCE parameters); extra parameters cannot override them. Once enabled, a "Sign in with " button appears on the sign-in page. After signing in at the provider, the user returns to GrantForge; accounts with two-step verification enabled must also enter a verification code. diff --git a/docs/content/en/guide/users.md b/docs/content/en/guide/users.md index fb3f1182..2d37170e 100644 --- a/docs/content/en/guide/users.md +++ b/docs/content/en/guide/users.md @@ -31,6 +31,7 @@ Creating a user requires a username (3–64 letters, digits, or `._@-`, unique a | Reset two-step verification | For a lost authenticator: turns off two-step verification for the account and ends its sessions | | View roles | The roles the account holds and the source of each one (direct assignment, group, department, position) | | View effective permissions | See [Permission explanation, impersonation, and audit](/en/guide/explain/) | +| Grant directly | Pick an application and allow menus, pages, buttons or APIs for this user alone, optionally until an expiry time | | Delete | Deletes the account and its department memberships; audit records are kept | The system account (the administrator created during initialization) and your own account cannot be disabled, locked, or deleted. diff --git a/docs/content/en/integration/java.md b/docs/content/en/integration/java.md index c960c829..53ff63f8 100644 --- a/docs/content/en/integration/java.md +++ b/docs/content/en/integration/java.md @@ -48,7 +48,7 @@ GrantForge requires PKCE for all clients; enable it for Spring Security login wi ## API permissions ```java -@RequirePermission("orders.delete") // 方法或类上;全部编码都需要 +@RequirePermission("orders.delete") // on a method or a class; every code is required @DeleteMapping("/api/orders/{id}") public void delete(@PathVariable long id) { ... } ``` @@ -63,16 +63,16 @@ grantForge.require("orders.read", "orders.export"); ## Data permissions -Declare it on the JPA entity; at startup the starter registers the entity with GrantForge using the confidential client, after which you can configure policies for `<应用编码>:order` in the role's data permissions: +Declare it on the JPA entity; at startup the starter registers the entity with GrantForge using the confidential client, after which you can configure policies for `:order` in the role's data permissions: ```java @Entity @GrantForgeEntity(code = "order", name = "Orders", owner = "ownerId", unit = "unitId", tenant = "tenantId") public class ShopOrder { - private long ownerId; // GrantForge 账号 ID,“本人”范围比较它 - private Long unitId; // GrantForge 部门 ID,部门范围比较它 - private String tenantId; // 可选:映射后,除“全部”外的规则都要求同租户 - @GrantForgeField("Status") private Status status; // 条件可测试的字段:文本、数字、布尔、枚举(选项)、时间 + private long ownerId; // GrantForge account ID, compared by the "own records" scope + private Long unitId; // GrantForge department ID, compared by the department scopes + private String tenantId; // optional: once mapped, every rule but "All" requires the same tenant + @GrantForgeField("Status") private Status status; // a field conditions can test: text, number, boolean, enum (choices), time @GrantForgeField("Total") private long total; } ``` @@ -81,7 +81,7 @@ At query time, combine the user's data scope like an ordinary `Specification`: ```java orders.findAll(scopes.scope(ShopOrder.class, DataAction.READ).and(myFilters)); -orders.findOne(scopes.scope(ShopOrder.class, DataAction.DELETE).and(byId(id))); // 范围外的行视为不存在 +orders.findOne(scopes.scope(ShopOrder.class, DataAction.DELETE).and(byId(id))); // rows outside the scope count as missing ``` The semantics match the GrantForge console: a row is available when an allow rule applies and no deny rule matches it; with no rules at all, nothing is available; "my department and below" is expanded by GrantForge into a list of department IDs. "Row belongs to the owner's department" is not supported yet. diff --git a/docs/content/en/integration/javascript.md b/docs/content/en/integration/javascript.md index 63ab54c6..0bb89c0f 100644 --- a/docs/content/en/integration/javascript.md +++ b/docs/content/en/integration/javascript.md @@ -19,11 +19,11 @@ import { GrantForgeAuth, GrantForgeClient } from '@grantforge/client' const auth = new GrantForgeAuth({ issuer: 'https://grantforge.example.com', clientId: 'gf_xxx', redirectUri: `${location.origin}/` }) if (new URLSearchParams(location.search).has('code')) { - const { returnTo } = await auth.handleRedirect() // 换取令牌并校验 nonce + const { returnTo } = await auth.handleRedirect() // exchanges the code for tokens and checks the nonce history.replaceState(null, '', returnTo ?? '/') } -if (!auth.signedIn) await auth.login(location.pathname) // 跳转 GrantForge 登录 -const token = await auth.accessToken() // 过期前自动续期;null 表示需要重新登录 +if (!auth.signedIn) await auth.login(location.pathname) // redirects to the GrantForge sign-in +const token = await auth.accessToken() // renewed before it expires; null means sign in again ``` Tokens are kept in sessionStorage and die with the tab. GrantForge does not issue refresh tokens to public clients, so once the access token expires the user must sign in again. @@ -46,9 +46,10 @@ app.use(createGrantForge(grantForge)) ``` ```vue - - - ``` + + + +``` `useGrantForge()` provides `authorization`, `error`, `refresh()`, `can()`, and `hasResource()`. Hiding buttons is a convenience only; the application's API must enforce the same permissions. diff --git a/docs/content/en/integration/open-api.md b/docs/content/en/integration/open-api.md index e006d648..38364f82 100644 --- a/docs/content/en/integration/open-api.md +++ b/docs/content/en/integration/open-api.md @@ -55,19 +55,19 @@ The application declares its data entities with its own confidential client (cli "entities": [ { "code": "order", - "name": "订单", + "name": "Order", "owned": true, "unitBased": false, "fields": [ - { "code": "status", "name": "状态", "type": "CHOICE", "choices": ["NEW", "PAID", "SHIPPED"] }, - { "code": "total", "name": "金额", "type": "NUMBER" } + { "code": "status", "name": "Status", "type": "CHOICE", "choices": ["NEW", "PAID", "SHIPPED"] }, + { "code": "total", "name": "Amount", "type": "NUMBER" } ] } ] } ``` -Once declared, the entities appear in the role's data-permission editor as `<应用编码>:<实体编码>` (for example `shop:order`). `owned` means each row has an owning account (enabling the "own records only" scope), and `unitBased` means each row belongs to a department (enabling the department scopes). Java applications do not have to write this request by hand; the starter declares entities automatically from `@GrantForgeEntity`. +Once declared, the entities appear in the role's data-permission editor as `:` (for example `shop:order`). `owned` means each row has an owning account (enabling the "own records only" scope), and `unitBased` means each row belongs to a department (enabling the department scopes). Java applications do not have to write this request by hand; the starter declares entities automatically from `@GrantForgeEntity`. ## Errors diff --git a/docs/content/en/integration/overview.md b/docs/content/en/integration/overview.md index 44bbdd1a..52deaa21 100644 --- a/docs/content/en/integration/overview.md +++ b/docs/content/en/integration/overview.md @@ -23,7 +23,7 @@ GrantForge plays two roles for business applications: | Client | Resource catalog → the application's "OAuth clients" | The identity the application uses to sign users in and obtain tokens. Browser applications use **public clients**, server-side applications use **confidential clients** | | scope | Client settings | `openid`, `profile`, and `email` for sign-in; `permissions` lets the token query permissions; `catalog` lets the application declare data entities as itself | | Roles and grants | Access control → Role management | Grant the application's resources to roles, then assign roles to users, groups, departments, or positions | -| Data policies | Role → Data permissions | Entities declared by the application (`<应用编码>:<实体>`) are configured just like the console's own entities: All, this tenant, own records only, department, specified departments, or by condition | +| Data policies | Role → Data permissions | Entities declared by the application (`:`) are configured just like the console's own entities: All, this tenant, own records only, department, specified departments, or by condition | Tenant administrators (those holding the system role) can grant any resource of a business application to roles in their own tenant; permissions of the console itself can still only be granted as far as the granter holds them. @@ -31,19 +31,19 @@ Tenant administrators (those holding the system role) can grant any resource of ```mermaid sequenceDiagram - participant B as 浏览器 - participant A as 业务应用 + participant B as Browser + participant A as Application participant G as GrantForge B->>G: /oauth2/authorize(PKCE) - G-->>B: 未登录时转到控制台登录页,登录后带回授权请求 - G-->>B: 回到应用的回调地址,带 code + G-->>B: Not signed in: the console sign-in page, then back to the authorization request + G-->>B: Back to the application's redirect URI, with the code B->>G: /oauth2/token(code + code_verifier) - G-->>B: 访问令牌、ID 令牌 + G-->>B: Access token, ID token B->>G: /api/v1/open/me/authorization(Bearer) - G-->>B: 角色、资源、API 权限(ETag) - B->>A: 调用应用 API(Bearer) - A->>G: /api/v1/open/me/authorization、/data-access(同一个令牌) - A-->>B: 只返回用户可用的数据 + G-->>B: Roles, resources, API permissions (ETag) + B->>A: Calls the application API (Bearer) + A->>G: /api/v1/open/me/authorization, /data-access (same token) + A-->>B: Only the data the user may see ``` ## Integration steps diff --git a/docs/content/en/reference/errors.md b/docs/content/en/reference/errors.md index bc58f839..cc032055 100644 --- a/docs/content/en/reference/errors.md +++ b/docs/content/en/reference/errors.md @@ -16,7 +16,7 @@ All error responses are RFC 9457 problem details: "type": "about:blank", "title": "Forbidden", "status": 403, - "detail": "你没有执行此操作的权限", + "detail": "You do not have permission to perform this action.", "code": "GF-SECURITY-002", "requestId": "0f6c1a…" } diff --git a/docs/content/en/start/concepts.md b/docs/content/en/start/concepts.md index 71680ed8..ca60546a 100644 --- a/docs/content/en/start/concepts.md +++ b/docs/content/en/start/concepts.md @@ -13,18 +13,18 @@ The GrantForge model can be summed up in one sentence: **accounts receive roles ```mermaid flowchart LR - A[账号] -->|直接分配| R[角色] - G[用户组] -->|分配| R - O[部门] -->|分配,可含下级| R - P[岗位] -->|分配| R - A -.属于.-> G - A -.属于.-> O - A -.任职.-> P - R -->|继承| R2[父角色] - R -->|授权 允许/拒绝| S[资源] - S --- M[菜单 · 页面 · 按钮] + A[Account] -->|direct assignment| R[Role] + G[Group] -->|assignment| R + O[Department] -->|assignment, sub-departments optional| R + P[Position] -->|assignment| R + A -. member of .-> G + A -. member of .-> O + A -. holds .-> P + R -->|inherits| R2[Parent role] + R -->|grants allow/deny| S[Resource] + S --- M[Menu · Page · Button] S --- API[API] - S --- D[数据实体 · 字段] + S --- D[Data entity · Field] ``` ## Tenants diff --git a/docs/content/en/start/quick-start.md b/docs/content/en/start/quick-start.md index a356a06a..3475f829 100644 --- a/docs/content/en/start/quick-start.md +++ b/docs/content/en/start/quick-start.md @@ -38,7 +38,7 @@ For the distribution package, logs are written to `logs/grantforge.log`; with Do ## 2. Complete Initialization -Open http://127.0.0.1:9999/ in a browser and the console goes straight to the setup page. Enter the token from the log, the organization name, and the username and password for the first administrator (at least 12 characters). +Open in a browser and the console goes straight to the setup page. Enter the token from the log, the organization name, and the username and password for the first administrator (at least 12 characters). ![First-run setup and login page](/screenshots/login.png) diff --git a/docs/content/es/changelog/2026.4.0.md b/docs/content/es/changelog/2026.4.0.md new file mode 100644 index 00000000..ef94a095 --- /dev/null +++ b/docs/content/es/changelog/2026.4.0.md @@ -0,0 +1,68 @@ +--- +title: "2026.4.0" +description: "GrantForge 2026.4.0 (2026-11-08): plugins de servicio y agentes para Elasticsearch, Apache Doris, MongoDB y Redis/Valkey, sincronización de la organización desde DingTalk, permisos directos de usuario, exportación e importación del catálogo de recursos y una CI que comprueba que cada idioma está traducido por completo." +--- + + +2026.4.0 amplía los permisos de sistemas externos a Elasticsearch, Apache Doris, MongoDB, y a Redis y Valkey. Los cuatro agentes se ejecutan junto al sistema y mantienen su propio control de acceso de acuerdo con las políticas. En identidad y autorización, esta versión añade la sincronización de la organización desde DingTalk, los permisos directos de usuario y la exportación e importación del catálogo de recursos. Todos los cambios están en la [comparación de GitHub](https://github.com/devlive-community/grantforge/compare/v2026.3.0...v2026.4.0). + +## Elasticsearch + +- Plugin de servicio de Elasticsearch: índices y flujos de datos, sus campos y el clúster; inicia sesión con contraseña, clave de API o certificado de cliente TLS; consulta [Elasticsearch](/es/plugins/elasticsearch/). +- Agente junto al clúster: mantiene para cada usuario un rol propio de Elasticsearch, asignado a los usuarios nativos y, mediante asignaciones de roles, a los usuarios de otros realms, con seguridad a nivel de campo y de documento cuando la licencia lo permite; los patrones de índice siguen siendo patrones, así que los índices nuevos son accesibles al instante; consulta [Agente de Elasticsearch](/es/external/elasticsearch-agent/). +- Los accesos proceden de los archivos del registro de auditoría. Las pruebas de integración cubren Elasticsearch 8.19, 9.3, 9.4 y 9.5. + +## Apache Doris + +- Plugin de servicio de Doris: catálogos, bases de datos, tablas, columnas y grupos de carga de trabajo; se conecta a uno o varios FE e inicia sesión con contraseña; consulta [Apache Doris](/es/plugins/doris/). +- Agente junto al clúster: las políticas se convierten en roles propios de Doris, permisos de columna, permisos sobre grupos de carga de trabajo y políticas de filas, concedidos a cada identidad del usuario; una base de datos o un catálogo entero solo se concede cuando Doris no daría más con ello; consulta [Agente de Apache Doris](/es/external/doris-agent/). +- Los accesos proceden de la tabla del registro de auditoría de Doris. Las pruebas de integración cubren Doris 2.1, 3.1, 4.0 y 4.1. + +## MongoDB + +- Plugin de servicio de MongoDB: bases de datos y colecciones; se conecta a un replica set o a un clúster fragmentado e inicia sesión con SCRAM, certificado de cliente, LDAP o Kerberos; consulta [MongoDB](/es/plugins/mongodb/). +- Agente junto al despliegue: mantiene un rol de MongoDB por usuario y sustituye sus privilegios en un solo paso; con los privilegios heredados que informa MongoDB, indica lo que conceden de verdad los roles integrados y personalizados ajenos a GrantForge; consulta [Agente de MongoDB](/es/external/mongodb-agent/). +- Los accesos proceden de los archivos del registro de auditoría de Enterprise o Percona, o del profiler. Las pruebas de integración cubren MongoDB 6.0, 7.0, 8.0 y 9.0, y Kerberos está verificado en Percona Server for MongoDB. + +## Redis y Valkey + +- Plugin de servicio de Redis: claves, canales y nodos; se conecta a un servidor independiente con sus réplicas o a un clúster, e inicia sesión como usuario de ACL, opcionalmente por TLS; consulta [Redis](/es/plugins/redis/). +- Agente junto al despliegue: en cada nodo mantiene los selectores de ACL de cada usuario de acuerdo con las políticas y los sustituye con un único comando; los permisos raíz siguen en manos de los operadores, y el agente pregunta a Redis con `ACL DRYRUN` si permiten accesos que las políticas deniegan; consulta [Agente de Redis](/es/external/redis-agent/). +- Las denegaciones proceden del registro de ACL de cada nodo. Las pruebas de integración cubren Redis 7.2, 7.4, 8.2 y 8.10, Valkey 8.1 y 9.1, además de un clúster de Redis 8.10. + +## Agentes + +- Los agentes de ClickHouse, PostgreSQL y OpenSearch y los nuevos agentes de esta versión que se ejecutan junto a un sistema comparten el nuevo núcleo `grantforge-agent-sync`: arranque, configuración, temporizadores, ciclo de sincronización, posiciones de lectura de la auditoría y seguimiento de archivos de registro. Cada agente conserva solo lo propio de su sistema; los formatos de configuración y de registro no cambian. + +## Identidad y organización + +- Fuentes de datos: la sincronización de la organización se configura aparte de las fuentes de identidad, y el primer adaptador es DingTalk, con su SDK oficial de servidor; tras iniciar sesión por SSO, los usuarios quedan vinculados automáticamente a sus departamentos y a su departamento principal mediante su correo de confianza; las tablas migran en las seis bases de datos admitidas, incluidas SQL Server y Oracle; consulta [Fuentes de identidad (LDAP y OIDC)](/es/guide/identity-sources/). +- Las fuentes de identidad OpenID Connect pueden sincronizar el teléfono y el avatar y añadir parámetros a la solicitud de autorización (como `provider_hint` de Casdoor); los parámetros propios del protocolo no se pueden sustituir. + +## Autorización y catálogo de recursos + +- Permisos directos de usuario: permiten a un único usuario menús, páginas, botones o API de una aplicación de negocio, opcionalmente hasta una fecha de caducidad; se calculan junto con los permisos de los roles, y una denegación de un rol sigue prevaleciendo; consulta [Usuarios](/es/guide/users/). +- El catálogo de recursos puede exportar el árbol de recursos de una aplicación como JSON e importarlo en otra aplicación: la importación se comprueba primero y, si hay un conflicto, no se aplica nada; los administradores de la plataforma pueden vaciar los árboles de aplicación que cumplan las condiciones; consulta [Catálogo de recursos y API](/es/guide/catalog/). + +## Servidor + +- Al arrancar, el servidor muestra un banner de GrantForge con su versión y la de Java en lugar del banner de Spring Boot. + +## Documentación y publicación + +- La pestaña de plugins empieza con una vista general, agrupa los sistemas por tipo en una barra lateral plegable e indica para cada sistema cómo aplica las políticas, si admite filtros de filas y enmascaramiento y qué versiones están verificadas; todos los README enumeran los componentes en el mismo orden. +- La CI comprueba que cada página está traducida por completo a cada idioma: los mismos párrafos, encabezados, tablas, bloques de código y enlaces que la página en chino, ningún texto en la escritura de otro idioma y nada copiado de la página en inglés; los 11 README pasan las mismas comprobaciones respecto al README en inglés. Se han completado todas las traducciones que faltaban y que encontró la comprobación. +- La comprobación de versiones mantiene al día la insignia de versión de cada README. +- Los flujos de publicación de la documentación y de las versiones ya no se ejecutan en los forks. +- El servidor `grantforge-server` ya no se publica en los repositorios de Maven: incluye todos los plugins y supera su límite de tamaño; se distribuye en el archivo de la versión y en la imagen. + +## Correcciones + +- Al cambiar el tipo de un recurso en el editor de recursos se borra el código de recurso que ya no corresponde. +- El último bloque de código de la guía de integración con JavaScript ya no se traga el texto que lo sigue, y la dirección de la consola en las páginas de Docker y de inicio rápido ya no convierte en enlace el texto siguiente. +- Dependencias actualizadas a versiones que corrigen avisos de seguridad: MariaDB Connector/J 3.5.11, OpenTelemetry 1.67.0, Guava 33.7.2, Snappy 1.1.10.9, y vue-i18n, glob y minimatch en la consola y el SDK. diff --git a/docs/content/es/deploy/docker.md b/docs/content/es/deploy/docker.md index 331bace4..8a88a220 100644 --- a/docs/content/es/deploy/docker.md +++ b/docs/content/es/deploy/docker.md @@ -43,7 +43,7 @@ docker compose -f deploy/compose/postgres.yml up -d docker compose -f deploy/compose/postgres.yml logs grantforge | grep "setup token" ``` -Después abre http://127.0.0.1:9999/. La contraseña de base de datos predeterminada que usan los ejemplos solo sirve para probar; cámbiala con `GRANTFORGE_DB_PASSWORD` antes de pasar a producción. El ejemplo de MySQL necesita que antes pongas `mysql-connector-j-.jar` en `deploy/compose/drivers/`. +Después abre . La contraseña de base de datos predeterminada que usan los ejemplos solo sirve para probar; cámbiala con `GRANTFORGE_DB_PASSWORD` antes de pasar a producción. El ejemplo de MySQL necesita que antes pongas `mysql-connector-j-.jar` en `deploy/compose/drivers/`. ## Helm diff --git a/docs/content/es/develop/development.md b/docs/content/es/develop/development.md index 76c33351..7722bde7 100644 --- a/docs/content/es/develop/development.md +++ b/docs/content/es/develop/development.md @@ -76,7 +76,7 @@ El script exige que el área de trabajo esté limpia, que la rama local no vaya - `script/ci/release.sh` compila el paquete publicado, una SBOM CycloneDX con solo las dependencias de publicación y `SHA256SUMS`; - sube imágenes multiarquitectura a `ghcr.io/devlive-community/grantforge`; -- publica los artefactos de Maven (con el paquete de código fuente y el Javadoc) en GitHub Packages; cuando el repositorio tiene configurados `CENTRAL_USERNAME`, `CENTRAL_PASSWORD` (token de Central Portal), `GPG_PRIVATE_KEY` y `GPG_PASSPHRASE`, los firma y los publica en Maven Central; +- publica los artefactos de Maven (con el paquete de código fuente y el Javadoc) en GitHub Packages; cuando el repositorio tiene configurados `CENTRAL_USERNAME`, `CENTRAL_PASSWORD` (token de Central Portal), `GPG_PRIVATE_KEY` y `GPG_PASSPHRASE`, los firma y los publica en Maven Central; el servidor `grantforge-server` incluye todos los plugins y supera el límite de tamaño de los repositorios, así que no se publica en ningún repositorio de Maven y solo se distribuye en el archivo de la versión y en la imagen; - crea una GitHub Release cuyo cuerpo recoge todos los commits desde la última versión publicada (una etiqueta `v*` o numérica como `1.0.6`), agrupados por nuevas funciones, correcciones, rendimiento, etc., con enlaces a los commits. Las versiones candidatas se marcan como preliminares y no actualizan el `latest` de las imágenes. Con el perfil `central` activado en local, por defecto no se publica (`central.skip=true`); solo el flujo de trabajo de publicación pasa explícitamente `-Dcentral.skip=false`. @@ -100,3 +100,5 @@ pnpm build # salida en docs/out ``` Las páginas son Markdown en `docs/content/` y la navegación está en `docs/lib/navigation.ts`. La referencia de la API y los códigos de error se generan al compilar a partir del contrato y del código fuente. Las capturas de pantalla las genera `script/docs/screenshots.sh` arrancando el servicio real, escribiendo datos de ejemplo y usando después Playwright. + +`pnpm test` también comprueba las traducciones: cada página en chino necesita una traducción a cada uno de los otros diez idiomas, con los mismos párrafos, niveles de encabezado, listas, tablas, bloques de código y enlaces que la página en chino. Una página no puede contener texto en una escritura que su idioma no usa (incluidos caracteres simplificados en chino tradicional y en japonés) ni frases copiadas sin cambios de la página en inglés. Los 11 README de la raíz del repositorio pasan las mismas comprobaciones respecto al README en inglés, y sus enlaces a este sitio deben apuntar a páginas existentes de su propio idioma. diff --git a/docs/content/es/external/hdfs-agent.md b/docs/content/es/external/hdfs-agent.md index bf31d8c4..93d976c7 100644 --- a/docs/content/es/external/hdfs-agent.md +++ b/docs/content/es/external/hdfs-agent.md @@ -9,71 +9,69 @@ description: Aplica políticas de rutas GrantForge con el agente NameNode corres project root for full license text. --> -El plug-in de servidor `grantforge-plugin-hdfs` define recursos y conexiones. Los agentes NameNode numerados para Hadoop 2.7, 2.10, 3.2, 3.3, 3.4 y 3.5 verifican los accesos mediante la SPI de su versión y comparten instantáneas firmadas, evaluación de políticas y envío de auditoría. +El plugin de servidor `grantforge-plugin-hdfs` ofrece de forma unificada el tipo de servicio `hdfs`, sus recursos y la configuración de conexión. Los agentes de NameNode se construyen por separado para cada versión de Hadoop, comprueban los accesos con `INodeAttributeProvider` y `AccessControlEnforcer` de su versión y comparten la lógica de políticas firmadas, caché, evaluación de políticas y auditoría. -La lógica HDFS compartida está en `agents/grantforge-agent-hdfs-common` y los adaptadores por versión en `agents/grantforge-agent-hdfs-`. Los adaptadores nativos compartidos forman el módulo Maven de producción `agents/grantforge-agent-hdfs-native`, con base Java 8 / Hadoop 2.7.7. Cada módulo numerado consume su dependencia Maven binaria y conserva sus puntos de entrada y callbacks específicos, sin recompilar las fuentes de producción compartidas. `core/grantforge-agent-core` sigue siendo la biblioteca común de protocolo y ejecución. +En el código fuente, el plugin de servidor está en `plugins/grantforge-plugin-hdfs`, los agentes de versión en `agents/grantforge-agent-hdfs-` y la lógica de producción común sin dependencia de Hadoop en `agents/grantforge-agent-hdfs-common`. La adaptación nativa compartida está en el módulo de producción de Maven `agents/grantforge-agent-hdfs-native`, con Java 8 / Hadoop 2.7.7 como base; cada módulo numerado la usa como dependencia binaria de Maven, conserva sus puntos de entrada y devoluciones de llamada propios y ya no vuelve a compilar el código de producción compartido. El protocolo, la caché de instantáneas y la infraestructura de auditoría siguen en `core/grantforge-agent-core`. -El plug-in del servidor conserva un solo tipo de servicio `hdfs` y una versión de cliente independiente de los agentes. Para Hadoop 2.x, usa `webhdfs://namenode:50070` o la dirección `swebhdfs://` correspondiente con HTTPS; Hadoop 3.x admite RPC `hdfs://` o WebHDFS. Las pruebas en contenedores cubren WebHDFS en 2.x y ambos protocolos en 3.x. RPC en 2.x no está certificado y el protocolo configurado no cambia automáticamente. +El plugin de servidor mantiene un único tipo `hdfs`, y la versión del cliente es independiente de la del agente. La conexión y la búsqueda de rutas en Hadoop 2.x usan `webhdfs://namenode:50070` (con HTTPS, la dirección `swebhdfs://` correspondiente); Hadoop 3.x admite RPC `hdfs://` o WebHDFS. La matriz de contenedores verifica WebHDFS en 2.x, y RPC y WebHDFS en 3.x; el RPC de 2.x no está certificado y el protocolo de conexión nunca cambia automáticamente. -Con las extensiones de atributos inode activadas, Apache Hadoop 2.7.7 genera un error nativo de puntero nulo cuando un usuario normal consulta `/`, antes del callback del agente. Usa directorios de datos como `/data` para las operaciones y `lookup.path` en esta versión. La prueba en contenedores verifica explícitamente esta limitación. +Con la extensión de atributos de inode activada, Apache Hadoop 2.7.7 provoca en su código nativo un error de puntero nulo cuando un usuario normal consulta directamente la ruta raíz `/`, antes incluso de la devolución de llamada del agente. En esta versión, usa directorios de datos reales como `/data` para las operaciones de datos y para el `lookup.path` del servicio; las pruebas mantienen una aserción propia para el fallo en la raíz. -## Versiones objetivo de los agentes HDFS +## Elegir la versión -| Base de Hadoop | Java del contenedor | Directorio del agente | -| --- | --- | --- | -| 2.7.7 | Java 8 | `agents/hdfs/2.7/` | -| 2.10.2 | Java 8 | `agents/hdfs/2.10/` | -| 3.2.4 | Java 8 | `agents/hdfs/3.2/` | -| 3.3.6 | Java 8 (imagen amd64) | `agents/hdfs/3.3/` | -| 3.4.3 | Java 11 | `agents/hdfs/3.4/` | -| 3.5.0 | Java 17 | `agents/hdfs/3.5/` | - -Selecciona `grantforge-agent-hdfs--.jar` de `agents/hdfs//` para la línea Hadoop del clúster. La lógica compartida apunta a Java 8 y el adaptador 3.5 a Java 17. +| Sufijo del módulo del agente | Apache Hadoop verificado | JVM del contenedor | Interfaz de autorización | Devolución de llamada de rutas de superusuario | +| --- | --- | --- | --- | --- | +| `2.7` | 2.7.7 | Java 8 | por parámetros | no | +| `2.10` | 2.10.2 | Java 8 | por parámetros | no | +| `3.2` | 3.2.4 | Java 8 | por parámetros | no | +| `3.3` | 3.3.6 | Java 8 | por contexto | no | +| `3.4` | 3.4.3 | Java 11 | por contexto, devoluciones de llamada de superusuario y de denegación | sí | +| `3.5` | 3.5.0 | Java 17 | por contexto, devoluciones de llamada de superusuario y de denegación | sí | -Hadoop 2.7, 2.10, 3.2 y 3.3 no ofrecen el callback de autorización de superusuario que usa el agente. Hadoop conserva el control de esos accesos; utiliza usuarios normales para acceder a datos sujetos a políticas de GrantForge. +Por ejemplo, para Hadoop 2.10.2 se instala `agents/hdfs/2.10/grantforge-agent-hdfs-2.10-.jar`. En cada NameNode se instala un único agente de versión; al actualizar se quita el jar anterior y el nombre de clase del provider en la configuración no cambia. Al arrancar, el agente comprueba la versión mayor y menor de Hadoop y las devoluciones de llamada necesarias, y no arranca si la versión no coincide; otros parches de la misma versión y las distribuciones de proveedores siguen necesitando una verificación propia. -## Relación con los permisos +El código principal de los agentes `common` y `2.7` a `3.4` usa bytecode de Java 8, y `3.5` usa Java 17. La tabla indica las JVM con las que se ejecutan realmente las pruebas de integración; no significa que 3.4.3 esté verificado con Java 8. La imagen de prueba oficial de 3.3.6 solo existe para amd64, así que en hosts ARM se ejecuta explícitamente con emulación amd64. -El comportamiento siguiente describe los accesos de usuarios normales. Las limitaciones de superusuario se indican arriba y en el apartado de callbacks de superusuario. +## Relación con los permisos nativos -El agente ejecuta primero la comprobación de permisos nativa de HDFS y después las políticas de GrantForge: el usuario tiene que cumplir al mismo tiempo los requisitos de los permisos nativos y de las políticas. Las políticas de permiso de GrantForge no sortean los permisos POSIX, las ACL, la comprobación del propietario ni el sticky bit; las políticas de denegación siempre deniegan. Los ajustes de permisos nativos se mantienen con las herramientas de administración de Hadoop. +El agente ejecuta primero las comprobaciones de permisos nativas de HDFS y después las políticas de GrantForge: el usuario debe cumplir tanto los permisos nativos como las políticas. Las políticas de permiso de GrantForge no se saltan los permisos POSIX, las ACL, las comprobaciones de propietario ni el sticky bit; las políticas de denegación siempre deniegan. Los permisos nativos se siguen gestionando con las herramientas de administración de Hadoop. -Por defecto `grantforge.hdfs.native.fallback=false`: cuando no hay una instantánea de políticas local, no hay una política coincidente o el agente aún no se ha iniciado, se deniega el acceso a los datos. Si se pone a `true`, los accesos que ninguna política decide usan los permisos nativos; las políticas de denegación explícita siguen vigentes. Cuando el servidor no está alcanzable de forma temporal, se sigue usando la última instantánea local que pasó la verificación de firma. +De forma predeterminada, `grantforge.hdfs.native.fallback=false`: sin instantánea local de políticas, sin política coincidente o mientras el agente no ha arrancado, se deniega el acceso a los datos. Con `true`, los accesos que ninguna política decide usan los permisos nativos; las denegaciones explícitas siguen vigentes. Si el servidor no está disponible temporalmente, se sigue usando la última instantánea local con firma verificada. -Las proyecciones de ancestros, destino, subárbol y rutas de instantáneas de una misma llamada de retorno de autorización usan la misma versión de la instantánea de políticas; las políticas actualizadas entran en vigor en la siguiente llamada, para no combinar reglas de permiso de versiones distintas. La auditoría de accesos registra la versión de política que realmente se usó. +Las proyecciones de ancestros, destino, subárboles y rutas de instantánea dentro de una misma devolución de llamada de autorización usan la misma versión de la instantánea de políticas; las políticas actualizadas se aplican desde la siguiente devolución de llamada, de modo que no se mezclan reglas de permiso de versiones distintas. La auditoría de accesos registra la versión de políticas usada realmente. -El agente comprueba los permisos `read`, `write` y `execute` que un usuario normal necesita para acceder al destino, y también los directorios padre, los directorios ancestros y los subdirectorios que hay que validar de forma recursiva. Operaciones como crear, borrar o renombrar afectan a varias rutas, así que las políticas de permiso deben cubrirlas todas. En modo estricto, no basta con la política `read` sobre el archivo de destino: hay que configurar al usuario la política `execute` sobre los directorios ancestros, por ejemplo permitir `execute` en `/` y marcar recursivo, y luego configurar permisos de lectura y escritura en el directorio de datos real. +El agente comprueba los permisos `read`, `write` y `execute` que un usuario normal necesita sobre el destino, y también el directorio padre, los directorios ancestros y los subdirectorios que requieren comprobación recursiva. Crear, eliminar, renombrar y otras operaciones afectan a varias rutas, y las políticas de permiso deben cubrirlas todas. En modo estricto no basta una política `read` sobre el archivo de destino: el usuario necesita también políticas `execute` sobre los directorios ancestros, por ejemplo permitir `execute` en `/` con recursión marcada, y después permisos de lectura y escritura sobre los directorios de datos reales. -Las rutas de instantáneas se comprueban tanto en la ruta realmente solicitada como en la ruta original sin `.snapshot/`, por ejemplo `/data/.snapshot/s1/secret` comprueba también `/data/secret`. Por eso la política de denegación sobre la ruta original también restringe la instantánea; además se pueden definir restricciones más estrictas para la ruta de instantánea explícita. Las consultas de metadatos mantienen la semántica de permisos de recorrido de directorios de HDFS. +Las rutas de instantánea se comprueban tanto con la ruta solicitada como con la ruta original sin `.snapshot/`; por ejemplo, `/data/.snapshot/s1/secret` comprueba también `/data/secret`. Por eso las políticas de denegación de la ruta original también restringen las instantáneas; además se pueden poner restricciones más estrictas en rutas de instantánea explícitas. Las consultas de metadatos siguen la semántica de recorrido de directorios de HDFS. -Una autorización recursiva comprueba como máximo `100000` inodos; al superar el límite se deniega la operación, para no asignar memoria sin límite dentro del NameNode. Las rutas muy largas se evalúan con la ruta completa; la visualización del recurso en la auditoría se limita a `1000` caracteres, y en el detalle de la solicitud se registran la longitud original y el resumen SHA-256. +Una autorización recursiva examina como máximo `100000` inodes; por encima de ese límite se deniega la operación para que el NameNode no reserve memoria sin límite. Las rutas muy largas se evalúan con la ruta completa; la visualización del recurso en la auditoría se limita a `1000` caracteres y los detalles de la solicitud registran la longitud original y un resumen SHA-256. -Los callbacks de superusuario siguientes corresponden a Hadoop 3.4 y 3.5. El superusuario de HDFS sigue administrándolo Hadoop. Las llamadas de retorno de superusuario con ruta pasan primero por la comprobación de superusuario de Hadoop y después se evalúan según los nombres de operación que proporciona Hadoop 3.4 / 3.5: la lectura de archivos y las consultas de metadatos piden `read`, la enumeración de directorios pide `read` + `execute` y las operaciones de modificación conocidas piden `write`. Las operaciones desconocidas, ausentes o que no se pueden deducir con exactitud (por ejemplo `checkAccess` y `concat`) piden de forma conservadora los tres permisos. +Hadoop 2.7, 2.10, 3.2 y 3.3 dejan pasar a los superusuarios antes de llamar al agente, que no puede controlar ni auditar esos accesos. En Hadoop 3.4 y 3.5, las devoluciones de llamada de superusuario con ruta pasan primero la comprobación nativa y después se comprueban las políticas según el nombre de la operación: leer archivos y consultar metadatos exige `read`, listar directorios `read` + `execute` y las operaciones de modificación conocidas `write`. Las operaciones desconocidas, ausentes o imposibles de deducir con precisión (como `checkAccess` y `concat`) exigen por precaución los tres permisos. -Las llamadas de retorno de superusuario no tienen el contexto completo de inodos y subárbol; las llamadas de administración del cluster sin ruta mantienen la comprobación nativa; no es posible restringir todas las operaciones recursivas del superusuario con políticas de subdirectorio. Quienes usan los datos deben hacerlo con usuarios Hadoop normales. +Las devoluciones de llamada de superusuario no tienen el contexto completo de inodes y subárboles; las llamadas de administración del clúster sin ruta mantienen la comprobación nativa, y las políticas de subdirectorios no pueden limitar todas las operaciones recursivas de un superusuario. Quienes usan los datos deben usar usuarios normales de Hadoop. ## Métricas -El agente informa de las métricas a través del sistema Metrics2 de Hadoop, con los mismos sinks que las métricas dfs del propio NameNode, y en el JMX del NameNode aparece como `Hadoop:service=NameNode,name=GrantForgeHdfsAgent` (el exportador JMX de Prometheus puede capturarlo directamente). Cada métrica lleva las etiquetas `instance` (nombre de la instancia del servicio de datos) y `agentVersion`, así que los dos NameNode de una HA se pueden consultar por separado. Si falla el registro de métricas solo se pierden las métricas: el agente registra un aviso y sigue ejecutando autorizaciones sin métricas. +El agente publica métricas con el sistema Metrics2 de Hadoop, con los mismos sinks que las métricas dfs del propio NameNode; en el JMX del NameNode aparecen como `Hadoop:service=NameNode,name=GrantForgeHdfsAgent` (el exportador JMX de Prometheus puede recogerlas directamente). Cada métrica lleva las etiquetas `instance` (nombre de la instancia del servicio de datos) y `agentVersion`, de modo que los dos NameNode de un par HA se pueden ver por separado. Si falla el registro de métricas, solo se pierden las métricas: el agente registra una advertencia y sigue autorizando sin ellas. -| Métrica | Explicación | +| Métrica | Descripción | | --- | --- | -| `Callbacks` | Número de llamadas de retorno de autorización que ejecuta el agente | -| `SuperuserCallbacks` | Número de llamadas de retorno de superusuario que ejecuta el agente (3.4 / 3.5) | -| `NativeDenies` | Número de accesos que Hadoop deniega antes de llegar al agente | -| `EvaluationFailures` | Número de llamadas de retorno que fallan cerradas por una excepción al evaluar las políticas | -| `DecisionsAllowed` / `DecisionsDenied` / `DecisionsUndetermined` | Número de permisos que las políticas deciden permitir / denegar / dejar sin decidir; lo no decidido también se deniega en modo estricto | -| `MissingSnapshots` | Número de llamadas de retorno atendidas sin una instantánea de políticas verificada | -| `SnapshotVersion` | Versión de la instantánea de políticas en uso; 0 significa que no hay | -| `QueuedEvents` / `DroppedEvents` | Número de eventos de auditoría pendientes de envío en memoria / número de eventos descartados por estar llenas la cola o el búfer de disco | +| `Callbacks` | Devoluciones de llamada de autorización ejecutadas por el agente | +| `SuperuserCallbacks` | Devoluciones de llamada de superusuario ejecutadas por el agente | +| `NativeDenies` | Accesos que Hadoop denegó antes de llegar al agente | +| `EvaluationFailures` | Devoluciones de llamada cerradas por fallo debido a una excepción al evaluar políticas | +| `DecisionsAllowed` / `DecisionsDenied` / `DecisionsUndetermined` | Permisos que las políticas permitieron / denegaron / dejaron sin decidir; los no decididos también se deniegan en modo estricto | +| `MissingSnapshots` | Devoluciones de llamada atendidas sin instantánea de políticas verificada | +| `SnapshotVersion` | Versión de la instantánea de políticas en uso; 0 si no hay ninguna | +| `QueuedEvents` / `DroppedEvents` | Eventos de auditoría pendientes de envío en memoria / descartados porque la cola o el búfer de disco estaban llenos | | `ServerReachable` | Si el último acceso al servidor de políticas tuvo éxito (1/0) | ## Despliegue -1. Añade un servicio `hdfs` en los servicios de datos de GrantForge, guarda la configuración y prueba la conexión; configura políticas de rutas para los nombres de usuario cortos, grupos de usuarios o roles reales de Hadoop. -2. En "Permisos sobre los datos → Agentes" emite un token para este servicio. Escribe el token original en un archivo local de cada NameNode, por ejemplo `/etc/hadoop/grantforge/token`, legible por el usuario con el que se ejecuta el NameNode. -3. Selecciona la línea Hadoop del clúster y copia `grantforge-agent-hdfs--.jar` de `agents/hdfs//` al classpath del NameNode, por ejemplo `$HADOOP_HOME/share/hadoop/hdfs/lib/`. Instala un único adaptador compatible. El jar incluye la lógica compartida, Jackson y la biblioteca de firma; el NameNode aporta Hadoop. -4. Configura las siguientes propiedades en el `hdfs-site.xml` de cada NameNode; los dos NameNode de una HA usan `instance` distintos y sus propios directorios de caché locales. +1. Añade un servicio `hdfs` en los servicios de datos de GrantForge, guarda la configuración y prueba la conexión; configura políticas de ruta para los nombres cortos reales de los usuarios de Hadoop, sus grupos o sus roles. +2. En "Permisos de datos → Agentes", emite un token para este servicio. Escribe el texto del token en un archivo local de cada NameNode, por ejemplo `/etc/hadoop/grantforge/token`, legible por el usuario que ejecuta el NameNode. +3. Según la tabla, elige el jar de agente correspondiente en `agents/hdfs//` del paquete de distribución y colócalo en el classpath del NameNode, por ejemplo en `$HADOOP_HOME/share/hadoop/hdfs/lib/`. El jar del agente incluye su propio motor de políticas, Jackson y la biblioteca de firma; las clases de Hadoop las aporta el NameNode. La versión del agente en el heartbeat incluye la versión del producto y la versión de Hadoop con la que se compiló. +4. Configura las siguientes propiedades en el `hdfs-site.xml` de cada NameNode; los dos NameNode de un par HA usan `instance` distintos y cada uno su propio directorio de caché local. ```xml @@ -102,58 +100,56 @@ El agente informa de las métricas a través del sistema Metrics2 de Hadoop, con ``` -5. Comprueba que `dfs.permissions.enabled=true` y que `dfs.namenode.inode.attributes.provider.bypass.users` está vacío; al arrancar, el agente rechaza las configuraciones que permitan sortear las llamadas de retorno de autorización. Reinicia el NameNode y después comprueba el latido y la versión de políticas en la página de agentes de GrantForge. El agente lee la configuración existente del NameNode; no modifica los atributos nativos de los inodos. +5. Comprueba que `dfs.permissions.enabled=true` y que `dfs.namenode.inode.attributes.provider.bypass.users` está vacío; el agente no arranca con una configuración que permita saltarse la devolución de llamada de autorización. Reinicia el NameNode y revisa el heartbeat y la versión de políticas en la página de agentes de GrantForge. El agente lee la configuración existente del NameNode y no modifica los atributos nativos de los inodes. -Para un primer despliegue puedes empezar con `native.fallback=true`, confirmar que la instantánea de políticas ya está sincronizada y completar los permisos de los directorios ancestros, y después pasar al modo estricto. El tipo de servicio vinculado al token tiene que ser `hdfs`; una configuración errónea o vinculada a otro tipo de servicio deniega el acceso. +En el primer despliegue puedes empezar con `native.fallback=true`, comprobar que la instantánea de políticas está sincronizada y completar los permisos de los directorios ancestros, y después pasar al modo estricto. El token debe estar vinculado al tipo de servicio `hdfs`; una configuración errónea o un vínculo con otro tipo de servicio provoca la denegación. -## Ajustes opcionales +## Configuración opcional -| Propiedad | Valor por defecto | Uso | +| Propiedad | Valor predeterminado | Uso | | --- | --- | --- | -| `grantforge.hdfs.connect.timeout.ms` | `5000` | Tiempo de espera para conectar con GrantForge | -| `grantforge.hdfs.read.timeout.ms` | `8000` | Tiempo de espera para leer la respuesta | -| `grantforge.hdfs.refresh.interval.ms` | `30000` | Intervalo de actualización de políticas cuando el servidor no está alcanzable, como mínimo `1000`; el latido normal usa el intervalo que aconseja el servidor | -| `grantforge.hdfs.signing.key.file` | sin definir | Archivo opcional con la clave pública de firma, con la clave pública X.509 Base64 que proporciona la consola; una vez configurado solo se aceptan firmas de esa clave pública | +| `grantforge.hdfs.connect.timeout.ms` | `5000` | Tiempo de espera de conexión con GrantForge | +| `grantforge.hdfs.read.timeout.ms` | `8000` | Tiempo de espera de lectura de la respuesta | +| `grantforge.hdfs.refresh.interval.ms` | `30000` | Intervalo de actualización de políticas mientras el servidor no está disponible, al menos `1000`; el heartbeat normal usa el intervalo que recomienda el servidor | +| `grantforge.hdfs.signing.key.file` | sin definir | Archivo opcional de clave pública de firma, con la clave pública X.509 en Base64 que ofrece la consola; si se define, solo se aceptan firmas de esa clave | | `grantforge.hdfs.audit.batch.size` | `500` | Número máximo de eventos por envío, rango `1..1000` | -| `grantforge.hdfs.audit.queue.capacity` | `10000` | Capacidad de la cola de auditoría en memoria, rango `1..1000000`, como mínimo capaz de albergar un lote; cuando la cola está llena, cuenta y descarta los eventos nuevos | -| `grantforge.hdfs.audit.flush.interval.ms` | `5000` | Intervalo de vaciado de la auditoría, entero positivo, máximo `2147483647`; reducirlo baja la latencia de envío | -| `grantforge.hdfs.audit.spool.limit.bytes` | `67108864` | Límite del búfer de disco cuando el servidor no está alcanzable, entero no negativo; `0` desactiva el búfer de disco | - -Cuando el acceso llega a rachas muy altas, puedes aumentar la cola de auditoría para reducir los desbordamientos de cola; acortar el intervalo de vaciado baja la latencia de la auditoría y también aumenta la frecuencia de envío. El límite del búfer de disco controla el espacio de disco que se usa durante cortes prolongados; si el búfer está desactivado o agotado, se pueden perder eventos. El envío de auditoría se ejecuta en segundo plano y no espera la respuesta del servidor de políticas. +| `grantforge.hdfs.audit.queue.capacity` | `10000` | Capacidad de la cola de auditoría en memoria, rango `1..1000000`, al menos un lote; cuando se llena, los eventos nuevos se cuentan y se descartan | +| `grantforge.hdfs.audit.flush.interval.ms` | `5000` | Intervalo de vaciado de la auditoría, entero positivo de como máximo `2147483647`; un valor menor reduce el retraso del envío | +| `grantforge.hdfs.audit.spool.limit.bytes` | `67108864` | Límite del búfer de disco mientras el servidor no está disponible, entero no negativo; `0` desactiva el búfer de disco | -Si no se configura una clave pública de firma, el agente la obtiene del servidor la primera vez y la guarda junto con la instantánea. El agente usa el nombre de usuario corto y los grupos que le pasa Hadoop; los roles y grupos adicionales vienen de la instantánea firmada; la asignación del nombre corto del principal de Kerberos la decide el `hadoop.security.auth_to_local` del cluster. +Ante picos de acceso, aumenta la cola de auditoría para que se desborde menos; acortar el intervalo de vaciado reduce el retraso de la auditoría, pero aumenta la frecuencia de los envíos. El límite del búfer de disco acota el espacio usado durante cortes largos; con el búfer desactivado o agotado se pueden perder eventos. La auditoría se envía en segundo plano y no espera la respuesta del servidor de políticas. -## Compilar y verificar desde el código fuente +Si no se configura una clave pública de firma, el agente la obtiene del servidor la primera vez y la guarda con la instantánea. El agente usa el nombre de usuario corto y los grupos que le pasa Hadoop; los roles y los grupos adicionales proceden de la instantánea firmada. La correspondencia entre principals de Kerberos y nombres cortos la determina `hadoop.security.auth_to_local` del clúster. -Los comandos muestran 3.5; sustituye la línea por 2.7, 2.10, 3.2, 3.3 o 3.4 para el adaptador deseado. +## Compilar desde el código fuente y verificar ```sh ./mvnw -pl agents/grantforge-agent-hdfs-3.5 -am package ./mvnw -pl plugins/grantforge-plugin-hdfs,agents/grantforge-agent-hdfs-3.5,core/grantforge-plugin-host -am test ``` -El artefacto del agente está en `agents/grantforge-agent-hdfs-3.5/target/grantforge-agent-hdfs-3.5-.jar`. Las pruebas unitarias cubren las llamadas de retorno de autorización del NameNode, la configuración, los metadatos de versión y las decisiones de políticas; las pruebas de WebHDFS y Kerberos arrancan servicios temporales locales. Antes de pasar a producción, además hay que verificar en el cluster de destino la lectura y escritura, la creación, el renombrado, el borrado recursivo, la conmutación de HA y el comportamiento de la caché tras perder la red. +El artefacto del agente está en `agents/grantforge-agent-hdfs-3.5/target/grantforge-agent-hdfs-3.5-.jar`. Las pruebas unitarias cubren las devoluciones de llamada de autorización del NameNode, la configuración, los metadatos de versión y las decisiones de las políticas; las pruebas de WebHDFS y Kerberos arrancan servicios locales temporales. Antes de pasar a producción, verifica también en el clúster de destino la lectura y escritura, la creación, el renombrado, el borrado recursivo, la conmutación HA y el comportamiento de la caché tras un corte. -La verificación de integración se ejecuta con `verify` mediante Testcontainers (las pruebas unitarias no arrancan un cluster; `verify` necesita un Docker daemon disponible): +La verificación de integración se ejecuta con Testcontainers en la fase `verify` (las pruebas unitarias no arrancan ningún clúster; `verify` necesita un demonio de Docker disponible): ```sh ./mvnw -pl agents/grantforge-agent-hdfs-3.5 -am verify -# usa el mismo punto de entrada que el nightly -bash script/ci/hdfs_integration.sh 3.5 -# Una línea o la matriz completa -bash script/ci/hdfs_integration.sh all +# el mismo punto de entrada que nightly +bash script/ci/hdfs_integration.sh +# verificar solo la línea de versiones indicada +bash script/ci/hdfs_integration.sh 2.10 ``` -Las pruebas usan una imagen de contenedor de Apache Hadoop de versión fija y copian el jar del agente realmente empaquetado al classpath del NameNode. Testcontainers crea una red aislada y gestiona el ciclo de vida de NameNode y DataNode, y verifica la lectura y escritura, la creación, el añadido, el renombrado, el borrado, la denegación recursiva y de instantáneas, los permisos nativos y la auditoría, la actualización de políticas, el reinicio del NameNode usando la caché firmada tras desconectar el servidor de políticas, y el modo estricto y el repliegue a permisos nativos cuando no hay instantánea. +Las pruebas usan imágenes de Apache Hadoop de versión fija; las imágenes antiguas se construyen a partir de una imagen base de Java 8 con resumen fijo y una distribución de Apache verificada con SHA-512. El jar real del agente de la versión correspondiente se coloca en el classpath del NameNode y se comprueban las versiones reales de Hadoop y de la JVM. Testcontainers crea una red aislada y gestiona el ciclo de vida del NameNode y del DataNode; se verifican la lectura y escritura, la creación, la adición, el renombrado, el borrado, las denegaciones recursivas y en instantáneas, los permisos nativos y la auditoría, la actualización de políticas, el reinicio del NameNode con la caché firmada tras desconectar el servidor de políticas, y el modo estricto y el recurso a los permisos nativos sin instantánea. -Hace falta un Docker daemon en ejecución y que se permita descargar las imágenes de prueba. Si Docker no está disponible, las pruebas fallan, no se omiten en silencio. El cliente de sistema de archivos se ejecuta dentro del contenedor de Hadoop, y el servicio HTTP de políticas usa el reenvío de puertos del host de Testcontainers; no hace falta un cluster Hadoop externo. Al terminar las pruebas se limpian los contenedores y la red de prueba, y los logs se guardan en `agents/grantforge-agent-hdfs-3.5/target/hdfs-testcontainers`. +Hace falta un demonio de Docker en ejecución y permiso para descargar las imágenes de prueba. Sin Docker, las pruebas fallan en lugar de omitirse en silencio. El cliente del sistema de archivos se ejecuta dentro del contenedor de Hadoop, y el servicio HTTP de políticas usa el reenvío de puertos del host de Testcontainers; no se necesita un clúster de Hadoop externo. Al terminar se eliminan los contenedores y la red de prueba, y los registros se guardan en `agents/grantforge-agent-hdfs-3.5/target/hdfs-testcontainers`. -Las pruebas de HA arrancan dos NameNode, un DataNode y un JournalNode, y configuran nombres de instancia y directorios de caché independientes para los dos agentes. Usan un cliente HDFS lógico para conmutar manualmente el nodo activo y verifican la lectura, la escritura y las políticas de denegación después de la conmutación; el único JournalNode es solo para las pruebas: no verifica la tolerancia a fallos por mayoría ni implica la conmutación automática por fallo con ZooKeeper. +La prueba de HA arranca dos NameNode, un DataNode y un JournalNode, y da a los dos agentes nombres de instancia y directorios de caché distintos. Conmuta a mano el nodo activo con un cliente HDFS lógico y verifica después la lectura, la escritura y las políticas de denegación; un único JournalNode solo sirve para la prueba, así que no se verifica la tolerancia a fallos por mayoría ni la conmutación automática con ZooKeeper. -La prueba de HA automática (Hadoop 2.10 a 3.5, Java 17 y 21) arranca ZooKeeper, tres JournalNodes, dos NameNodes cada uno con su ZKFC y un DataNode. Verifica que, al detener el NameNode activo, ZooKeeper hace que el otro tome el relevo y siga aplicando las políticas, con las denegaciones atribuidas a su propia instancia; que las políticas publicadas mientras un NameNode no está se aplican cuando vuelve como standby y siguen aplicándose tras devolverle el rol, nunca una versión anterior; que sin el servidor de políticas el NameNode que toma el relevo sigue aplicando la instantánea que tiene y envía su auditoría cuando el servidor vuelve; y que con uno de tres JournalNodes detenido las escrituras siguen funcionando, mientras que con dos detenidos la escritura falla y el NameNode activo se detiene en lugar de seguir sin quórum. Una variante con Kerberos, solo en Hadoop 3.5.0, añade JournalNodes que inician sesión con un keytab y solo sirven HTTPS, ZKFC que se autentican en ZooKeeper con SASL y znodes de elección que solo puede usar el principal de los NameNodes (un cliente de ZooKeeper sin autenticar ni siquiera puede leerlos); verifica que el otro NameNode toma el relevo cuando se detiene el activo y sigue aplicando las políticas a los usuarios de Kerberos, que un cliente sin ticket se rechaza en todo momento y que el rol vuelve cuando regresa el primer NameNode. Los JournalNodes se ejecutan con un principal cuyo nombre corto es el de los NameNodes, porque un JournalNode solo entrega edits al principal completo de un NameNode o a un solicitante con su propio nombre corto, y la autenticación HTTP solo le da nombres cortos. La HA automática en Hadoop 2.7 no está verificada: su cliente solo conmuta mientras se resuelve el nombre de host de cada NameNode, y el de un contenedor detenido deja de resolverse, así que abandona con UnknownHostException en lugar de probar el otro NameNode. +La prueba de HA automática (Hadoop 2.10 a 3.5, Java 17 y 21) arranca ZooKeeper, tres JournalNode, dos NameNode con su propio ZKFC y un DataNode. Verifica que, al detener directamente el NameNode activo, ZooKeeper elige otro nodo, el nuevo nodo activo sigue aplicando las políticas y los eventos de denegación pertenecen a su instancia; que las políticas nuevas publicadas mientras un nodo estaba fuera se aplican cuando vuelve como Standby, y que tras volver a conmutar se sigue aplicando la versión nueva en lugar de la antigua; que, mientras el servicio de políticas no está disponible, el nodo que toma el relevo sigue con la instantánea ya cargada y reenvía la auditoría cuando el servicio se recupera; y que con uno de los tres JournalNode detenido las escrituras siguen funcionando, mientras que con dos fallan y el NameNode activo se detiene en lugar de continuar sin mayoría. También hay una variante con Kerberos (solo Hadoop 3.5.0): los JournalNode también inician sesión con keytab y solo abren HTTPS, los ZKFC se autentican ante ZooKeeper con SASL y solo el principal del NameNode puede acceder al nodo de elección (un cliente de ZooKeeper no autenticado no puede ni leer); la prueba verifica que, al detener el NameNode activo, otro nodo toma el relevo y sigue aplicando las políticas a los usuarios de Kerberos, que los clientes sin ticket siempre se rechazan y que se puede volver a conmutar cuando regresa el nodo original. Los JournalNode se ejecutan con un principal del mismo nombre corto que el del NameNode, porque un JournalNode solo entrega los edits al principal completo del NameNode o a solicitantes con su mismo nombre corto, y la autenticación HTTP solo proporciona el nombre corto. La HA automática de Hadoop 2.7 no está verificada: su cliente solo conmuta si puede resolver los nombres de host de todos los NameNode; el nombre del contenedor detenido en la prueba deja de resolverse, así que el cliente informa directamente de una UnknownHostException sin probar el otro nodo. -La prueba de Kerberos se ejecuta en las seis líneas (Java 17 y 21): el KDC corre en la JVM de la prueba, y el NameNode y el DataNode arrancan en modo seguro con sus propios keytabs; el DataNode transfiere datos solo tras SASL y sirve solo HTTPS, y WebHDFS usa SPNEGO. Verifica que las políticas de GrantForge se aplican a lecturas y escrituras una vez que los principals se asignan a nombres cortos, que un usuario sin política es rechazado, que las denegaciones se auditan con el nombre corto, que un cliente sin ticket es rechazado sin volver a la autenticación simple y que un NameNode reiniciado vuelve a iniciar sesión y sigue aplicando las políticas. +La prueba de Kerberos se ejecuta en las seis líneas de versiones (Java 17 y 21): el KDC se ejecuta dentro de la JVM de prueba, el NameNode y el DataNode arrancan en modo seguro con su propio keytab, la transferencia de datos del DataNode exige SASL y solo abre HTTPS, y WebHDFS usa SPNEGO. La prueba verifica que, tras asignar el principal a su nombre corto, las políticas de GrantForge se aplican a la lectura y la escritura, que los usuarios sin política se rechazan, que las denegaciones se auditan con el nombre corto, que los clientes sin ticket se rechazan sin recurrir a la autenticación simple y que el NameNode vuelve a iniciar sesión tras reiniciarse y sigue aplicando las políticas. -Las pruebas unitarias nativas compartidas se ejecutan en `agents/grantforge-agent-hdfs-native/src/test`, sin compilarse en los seis adaptadores. Las pruebas de callbacks específicos permanecen en sus módulos numerados. Las fuentes compartidas de pruebas de contenedores en `agents/grantforge-agent-hdfs-common/src/test/shared` siguen compilándose en los módulos de producción numerados. No existe un proyecto Maven independiente para pruebas; Testcontainers sigue siendo una dependencia de test. Nightly prueba las seis versiones Hadoop con hosts Java 17/21; la JVM de cada contenedor sigue la matriz anterior. Se conservan informes y logs. +El código fuente de las pruebas está en `src/test` de los módulos de producción reales. Las pruebas unitarias comunes de políticas se ejecutan en common y las pruebas unitarias nativas compartidas en `agents/grantforge-agent-hdfs-native/src/test`; ya no se compilan en los seis agentes de versión. Las pruebas de devolución de llamada de cada versión siguen en su módulo numerado, y el código compartido de pruebas de contenedores `agents/grantforge-agent-hdfs-common/src/test/shared` se sigue compilando en cada módulo numerado; no hay un proyecto de pruebas de Maven aparte. Testcontainers solo está en el ámbito test. Nightly verifica las seis versiones de Hadoop en hosts con Java 17 y 21, y los contenedores de Hadoop usan las JVM de la tabla. Hoy los contenedores cubren la autenticación Simple, la HA manual, Kerberos y HTTPS en las seis líneas de versiones, la HA automática de 2.10 a 3.5 y la HA automática con Kerberos en Hadoop 3.5.0; la HA automática de 2.7, la HA automática con Kerberos en otras versiones y los parches de los proveedores siguen necesitando verificación en el entorno de destino. -Los puntos de entrada de extensión de Hadoop y la semántica de permisos se describen en [Apache Hadoop 3.5.0 API](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/build/source/hadoop-hdfs-project/hadoop-hdfs/target/api/org/apache/hadoop/hdfs/server/namenode/INodeAttributeProvider.html) y en [HDFS Permissions Guide](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/HdfsPermissionsGuide.html). +Los puntos de extensión de Hadoop y la semántica de permisos se describen en la [API de Apache Hadoop 3.5.0](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/build/source/hadoop-hdfs-project/hadoop-hdfs/target/api/org/apache/hadoop/hdfs/server/namenode/INodeAttributeProvider.html) y en la [guía de permisos de HDFS](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/HdfsPermissionsGuide.html). diff --git a/docs/content/es/guide/catalog.md b/docs/content/es/guide/catalog.md index 7af85d16..4b706157 100644 --- a/docs/content/es/guide/catalog.md +++ b/docs/content/es/guide/catalog.md @@ -22,6 +22,12 @@ description: Mantén el árbol de recursos y las dependencias de cada aplicació - **Clientes OAuth**: registra clientes para aplicaciones de negocio; consulta [OAuth 2.1 y OpenID Connect](/es/integration/oauth/). - **Campos**: al seleccionar un campo se muestra en qué interfaces aparece (las que lo devuelven o lo reciben). +En el catálogo de una aplicación con recursos, pulsa **Exportar** para descargar un archivo JSON de referencia para el desarrollo o para herramientas de IA. Contiene la información de la aplicación, todos los recursos organizados por jerarquía y sus dependencias, con los códigos de los recursos en ambos extremos; la búsqueda y los filtros no cambian lo exportado. `formatVersion` indica la versión del formato de exportación. + +Elige la aplicación de destino, pulsa **Importar**, sube un archivo de exportación de la versión 1 y haz primero la comprobación previa. La importación reutiliza los recursos con el mismo código en la misma aplicación solo si coinciden el tipo y el padre, y no sobrescribe la configuración existente; un conflicto de tipo, padre o clase de dependencia anula toda la importación. Los recursos y dependencias nuevos se escriben juntos tras confirmar, y repetir una importación no crea duplicados. Las aplicaciones integradas no admiten importación; las dependencias declaradas pasan a ser manuales tras importarlas. Importar recursos requiere el permiso de crear recursos, e importar dependencias requiere además el de modificarlos. + +La exportación solo contiene lo registrado en GrantForge: `route` de las páginas y recursos similares es la ruta del frontend, y `code` de los recursos de API es el código de permiso. Los métodos HTTP y rutas de las API de negocio externas no se deducen si no están registrados. El JSON es una instantánea del momento de la exportación y no incluye las concesiones de ningún usuario; vuelve a exportar cuando cambien los recursos. + ## Catálogo de API **Gestión de plataforma → Catálogo de API** enumera todas las interfaces que el servidor registra solo al arrancar y lo que exige cada una para acceder: ser pública, bastar con iniciar sesión o necesitar un código de permiso. Las interfaces que requieren autorización se agrupan en el catálogo de recursos por su código de permiso, y los roles citan esos permisos al autorizarse. Cuando una interfaz se añade, se retira o cambia su código de permiso, aparece como "cambio pendiente de confirmar" y el catálogo se actualiza al confirmarlo. diff --git a/docs/content/es/guide/explain.md b/docs/content/es/guide/explain.md index 936a3c92..5bb5b31c 100644 --- a/docs/content/es/guide/explain.md +++ b/docs/content/es/guide/explain.md @@ -13,6 +13,8 @@ description: Consulta qué puede hacer una persona y por qué, simula cambios de En **Gestión de usuarios**, haz clic en "Ver permisos vigentes" en la fila de un usuario para ver todo lo que puede hacer ahora mismo: roles, menús y botones, API, alcance sobre los datos y campos restringidos. +Elige una aplicación para ver sus recursos y permisos de API vigentes; "Por qué" muestra si proceden de roles o de permisos directos del usuario. Los roles, alcances de datos y permisos de campo muestran la configuración de toda la cuenta. Al simular que se añaden o quitan roles, los permisos directos existentes se mantienen. + ![Permisos vigentes](/screenshots/user-permissions.png) Haz clic en **Explicación** en cualquiera de esos elementos y GrantForge responderá "por qué puede hacerlo": a partir de qué rol, de qué asignaciones (directa, mediante grupo, departamento o puesto) y de qué recursos se deduce. Cuando no pueda, indicará si es que no tiene autorización o si lo bloquea alguna denegación. diff --git a/docs/content/es/guide/identity-sources.md b/docs/content/es/guide/identity-sources.md index f7537381..32c52807 100644 --- a/docs/content/es/guide/identity-sources.md +++ b/docs/content/es/guide/identity-sources.md @@ -44,6 +44,11 @@ Elige el tipo "OpenID Connect" para conectar con Keycloak, Azure AD, Okta, otra | ID / secreto del cliente | El cliente registrado en el proveedor; si no hay secreto, se usa como cliente público con PKCE | | URL de retorno | La `/api/v1/auth/federated/callback/` que se muestra en la página debe registrarse en el cliente del proveedor | | Ámbitos y declaraciones | De forma predeterminada `openid profile email`; el nombre de usuario, el nombre visible y el correo se toman de las declaraciones `preferred_username`, `name` y `email` | +| Declaración del teléfono | Lee `phone` de forma predeterminada (Casdoor); otros proveedores pueden usar `phone_number`. Para sincronizar el teléfono, el ámbito debe incluir `phone`. Si el proveedor no devuelve ningún número al iniciar sesión, se conserva el existente | +| Dirección del avatar | Se sincroniza desde la declaración estándar de OIDC `picture`; el ámbito debe incluir `profile`. Si no se devuelve, se conserva la dirección existente | +| Parámetros adicionales de la solicitud de autorización | Objeto JSON opcional cuyos valores deben ser cadenas. Por ejemplo, en Casdoor `{"provider_hint":"github"}` elige un proveedor configurado. Los parámetros aparecen en la dirección del navegador, así que nunca pongas secretos en ellos | + +GrantForge gestiona los parámetros esenciales del protocolo (incluidos `state`, `nonce`, `redirect_uri` y los de PKCE); los parámetros adicionales no pueden sustituirlos. Una vez activada, en la página de inicio de sesión aparece el botón "Iniciar sesión con ". El usuario inicia sesión en el proveedor y vuelve a GrantForge; las cuentas con la verificación en dos pasos activada deben introducir además un código de verificación. @@ -54,3 +59,26 @@ Una vez activada, en la página de inicio de sesión aparece el botón "Iniciar - Una fuente de identidad que no crea cuentas automáticamente solo permite iniciar sesión a los usuarios ya asociados. - Tras deshabilitar una fuente de identidad, sus usuarios no pueden iniciar sesión; una fuente de identidad que aún usan cuentas no se puede eliminar. - A las cuentas de una fuente de identidad se les pueden asignar roles y activar la verificación en dos pasos con total normalidad. + +La sincronización de la estructura organizativa se configura en una fuente de datos aparte, sin cambiar los ajustes de inicio de sesión OIDC. + +## Fuentes de datos y sincronización de la organización + +Las fuentes de datos sincronizan la estructura organizativa y las fuentes de identidad siguen encargándose del inicio de sesión OIDC o LDAP. Se configuran por separado: deshabilitar una fuente de datos no afecta al inicio de sesión OIDC. + +1. Configura el inicio de sesión OpenID Connect existente en "Control de acceso → Fuentes de identidad". +2. En "Control de acceso → Fuentes de datos", añade una fuente de DingTalk y elige la fuente de identidad OIDC asociada. +3. Introduce CorpId, AppKey, AppSecret y el ID del departamento raíz (normalmente `1`). El AppSecret se cifra por separado, la API nunca lo devuelve en claro y, si al editar dejas el campo vacío, se conserva el valor anterior. +4. Puedes montarla bajo un departamento local. El intervalo de sincronización es de 30 minutos de forma predeterminada y admite de 15 a 10080 minutos; vacío significa solo sincronización manual. +5. Después de guardar, prueba la conexión y ejecuta la primera sincronización. La aplicación de DingTalk necesita permiso de lectura sobre los departamentos y el directorio de empleados, y su alcance autorizado debe cubrir el subárbol elegido. +6. Cuando un usuario inicia sesión por SSO, el empleado se busca por el correo OIDC de confianza y sus departamentos y su departamento principal se rellenan automáticamente. El correo debe administrarlo un administrador, no debe permitir que un empleado suplante a otro y debe coincidir con el correo de DingTalk. Si falta el correo del empleado, se usa el correo corporativo. + +El inicio de sesión solo lee la instantánea local del directorio y no consulta a DingTalk. Con MFA, la pertenencia se actualiza tras superar el segundo paso. Las cuentas que inician sesión antes de la primera sincronización guardan el correo de confianza y se vinculan cuando termina la sincronización; el correo del perfil, editable localmente, nunca se usa para la correspondencia. Un correo ausente, repetido o ya vinculado a otra cuenta no crea vínculos nuevos. + +El vínculo con un empleado se basa en su ID externo y no pasa a otro empleado cuando cambia el correo. La sincronización cubre la pertenencia a varios departamentos; el departamento principal sigue siendo el anterior mientras sea válido y, si no, se elige por el orden estable de los ID de departamento. Se conservan los departamentos locales, los grupos de usuarios, los puestos y las asignaciones directas de roles. Al asignar un rol a un departamento sincronizado, los usuarios lo heredan según las reglas existentes, y un traslado actualiza sus permisos. + +En cuanto existen una instantánea de la organización o cuentas pendientes de vincular, la fuente de identidad asociada, la empresa, la aplicación, el departamento raíz y el punto de montaje quedan fijos. Los departamentos y pertenencias sincronizados son de solo lectura y no se pueden sobrescribir a mano desde la gestión de usuarios o de la organización. Los departamentos que desaparecen se conservan para proteger los permisos existentes; cuando un empleado sale del directorio autorizado, se quitan las pertenencias que administra esta fuente, pero su cuenta no se deshabilita automáticamente. Una fuente de datos con departamentos propios no se puede eliminar directamente, pero sí deshabilitar. + +La sincronización obtiene todas las páginas antes de aplicar el resultado de forma atómica. Ante errores de lectura, jerarquías anómalas o cambios del alcance autorizado, se mantiene la última instantánea correcta; los administradores revisan el resultado con el resumen de sincronización, la lista de incidencias y el registro de auditoría. Si el alcance cambió, restablece primero el alcance aprobado y luego vuelve a sincronizar. + +Se usa el [SDK oficial de servidor de DingTalk](https://open.dingtalk.com/document/development/download-the-server-side-sdk), sin llamadas HTTP propias. Los adaptadores de proveedor producen instantáneas neutras de departamentos y empleados, de modo que un futuro adaptador de Feishu podrá reutilizar la misma lógica de sincronización, vinculación y actualización de permisos. diff --git a/docs/content/es/guide/users.md b/docs/content/es/guide/users.md index 212d6126..0d473f10 100644 --- a/docs/content/es/guide/users.md +++ b/docs/content/es/guide/users.md @@ -31,6 +31,7 @@ Busca por nombre de usuario, nombre visible o correo electrónico, filtra por es | Restablecer verificación en dos pasos | Para cuando se pierde el autenticador: desactiva la verificación en dos pasos de la cuenta y cierra sus sesiones | | Ver roles | Los roles que tiene la cuenta y el origen de cada uno (asignación directa, grupo de usuarios, departamento, puesto) | | Ver permisos vigentes | Ver [Explicar, simular y auditar los permisos](/es/guide/explain/) | +| Conceder permisos directos | Elegir una aplicación de negocio y permitir solo a este usuario menús, páginas, botones o API, con fecha de caducidad opcional | | Eliminar | Elimina la cuenta y sus relaciones con los departamentos; los registros de auditoría se conservan | La cuenta del sistema (el administrador creado durante la inicialización) y tu propia cuenta no se pueden deshabilitar, bloquear ni eliminar. @@ -38,3 +39,5 @@ La cuenta del sistema (el administrador creado durante la inicialización) y tu ## Registro automático Está desactivado de forma predeterminada. Después de establecer `grantforge.security.registration-enabled=true`, en la página de inicio de sesión aparece el acceso "Crear cuenta"; las cuentas registradas entran en el inquilino indicado en `grantforge.security.registration-tenant` y son cuentas normales sin ningún rol. + +Los permisos directos se calculan junto con los de los roles; una denegación de un rol sigue prevaleciendo y las dependencias de recursos se deducen como siempre. La separación de funciones basada en roles no se aplica a los permisos directos; no conceden políticas de filas ni de campos y no se pueden usar para la aplicación de la consola. Solo pueden gestionarlos los administradores con permiso para asignar roles y para modificar al usuario de destino. diff --git a/docs/content/es/integration/javascript.md b/docs/content/es/integration/javascript.md index 0f6c78f3..8e8a194a 100644 --- a/docs/content/es/integration/javascript.md +++ b/docs/content/es/integration/javascript.md @@ -48,7 +48,8 @@ app.use(createGrantForge(grantForge)) ```vue - ``` + +``` `useGrantForge()` proporciona `authorization`, `error`, `refresh()`, `can()` y `hasResource()`. Ocultar botones es solo una comodidad: la API de la aplicación debe comprobar los permisos igualmente. diff --git a/docs/content/es/start/quick-start.md b/docs/content/es/start/quick-start.md index 6d8eb908..3499f7ca 100644 --- a/docs/content/es/start/quick-start.md +++ b/docs/content/es/start/quick-start.md @@ -38,7 +38,7 @@ Los registros del paquete publicado están en `logs/grantforge.log`; con Docker ## 2. Completar la inicialización -Abre http://127.0.0.1:9999/ en el navegador y la consola entrará automáticamente en la página de inicialización. Rellena el token que aparece en el registro, el nombre de la organización y el usuario y la contraseña del primer administrador (al menos 12 caracteres). +Abre en el navegador y la consola entrará automáticamente en la página de inicialización. Rellena el token que aparece en el registro, el nombre de la organización y el usuario y la contraseña del primer administrador (al menos 12 caracteres). ![Página de inicialización y de inicio de sesión](/screenshots/login.png) diff --git a/docs/content/fr/changelog/2026.4.0.md b/docs/content/fr/changelog/2026.4.0.md new file mode 100644 index 00000000..69180c9c --- /dev/null +++ b/docs/content/fr/changelog/2026.4.0.md @@ -0,0 +1,68 @@ +--- +title: "2026.4.0" +description: "GrantForge 2026.4.0 (2026-11-08) : plug-ins de service et agents pour Elasticsearch, Apache Doris, MongoDB et Redis/Valkey, synchronisation de l’organisation depuis DingTalk, autorisations directes des utilisateurs, export et import du catalogue des ressources, et une CI qui vérifie que chaque langue est entièrement traduite." +--- + + +2026.4.0 étend les autorisations des systèmes externes à Elasticsearch, Apache Doris, MongoDB, ainsi qu’à Redis et Valkey. Les quatre agents s’exécutent à côté du système et alignent son propre contrôle d’accès sur les politiques. Côté identité et autorisation, cette version ajoute la synchronisation de l’organisation depuis DingTalk, les autorisations directes des utilisateurs, ainsi que l’export et l’import du catalogue des ressources. Toutes les modifications figurent dans la [comparaison sur GitHub](https://github.com/devlive-community/grantforge/compare/v2026.3.0...v2026.4.0). + +## Elasticsearch + +- Plug-in de service Elasticsearch : index et flux de données, leurs champs et le cluster ; connexion par mot de passe, clé d’API ou certificat client TLS ; voir [Elasticsearch](/fr/plugins/elasticsearch/). +- Agent à côté du cluster : maintient pour chaque utilisateur un rôle Elasticsearch propre, attribué aux utilisateurs natifs et, par des mappages de rôles, aux utilisateurs des autres realms, avec la sécurité au niveau des champs et des documents lorsque la licence le permet ; les motifs d’index restent des motifs, de sorte que les nouveaux index sont accessibles aussitôt ; voir [Agent Elasticsearch](/fr/external/elasticsearch-agent/). +- Les accès proviennent des fichiers du journal d’audit. Les tests d’intégration couvrent Elasticsearch 8.19, 9.3, 9.4 et 9.5. + +## Apache Doris + +- Plug-in de service Doris : catalogues, bases de données, tables, colonnes et groupes de charge de travail ; se connecte à un ou plusieurs FE et s’authentifie par mot de passe ; voir [Apache Doris](/fr/plugins/doris/). +- Agent à côté du cluster : les politiques deviennent les rôles propres de Doris, des droits sur les colonnes, des droits sur les groupes de charge de travail et des politiques de lignes, accordés à chaque identité de l’utilisateur ; une base de données ou un catalogue entier n’est accordé que si Doris n’en donnerait pas davantage ; voir [Agent Apache Doris](/fr/external/doris-agent/). +- Les accès proviennent de la table du journal d’audit de Doris. Les tests d’intégration couvrent Doris 2.1, 3.1, 4.0 et 4.1. + +## MongoDB + +- Plug-in de service MongoDB : bases de données et collections ; se connecte à un replica set ou à un cluster shardé et s’authentifie par SCRAM, certificat client, LDAP ou Kerberos ; voir [MongoDB](/fr/plugins/mongodb/). +- Agent à côté du déploiement : maintient un rôle MongoDB par utilisateur et remplace ses privilèges en une seule étape ; à partir des privilèges hérités que MongoDB indique, il signale ce que les rôles intégrés et personnalisés hors de GrantForge accordent réellement ; voir [Agent MongoDB](/fr/external/mongodb-agent/). +- Les accès proviennent des fichiers du journal d’audit d’Enterprise ou de Percona, ou du profileur. Les tests d’intégration couvrent MongoDB 6.0, 7.0, 8.0 et 9.0, et Kerberos est vérifié sur Percona Server for MongoDB. + +## Redis et Valkey + +- Plug-in de service Redis : clés, canaux et nœuds ; se connecte à un serveur seul et à ses réplicas ou à un cluster, et s’authentifie comme utilisateur ACL, éventuellement via TLS ; voir [Redis](/fr/plugins/redis/). +- Agent à côté du déploiement : aligne sur chaque nœud les sélecteurs ACL de chaque utilisateur sur les politiques et les remplace par une seule commande ; les droits racine restent du ressort des exploitants, et l’agent demande à Redis avec `ACL DRYRUN` s’ils permettent des accès que les politiques refusent ; voir [Agent Redis](/fr/external/redis-agent/). +- Les refus proviennent du journal ACL de chaque nœud. Les tests d’intégration couvrent Redis 7.2, 7.4, 8.2 et 8.10, Valkey 8.1 et 9.1, ainsi qu’un cluster Redis 8.10. + +## Agents + +- Les agents ClickHouse, PostgreSQL et OpenSearch et les nouveaux agents de cette version qui s’exécutent à côté d’un système partagent le nouveau cœur `grantforge-agent-sync` : lancement, réglages, minuteries, boucle de synchronisation, positions de lecture de l’audit et suivi des fichiers journaux. Chaque agent ne garde que ce qui est propre à son système ; les formats des réglages et des journaux ne changent pas. + +## Identité et organisation + +- Sources de données : la synchronisation de l’organisation se configure à part des sources d’identité, et le premier adaptateur est DingTalk, avec son SDK serveur officiel ; après une connexion SSO, les utilisateurs sont associés automatiquement à leurs départements et à leur département principal grâce à leur adresse e-mail de confiance ; les tables migrent sur les six bases de données prises en charge, SQL Server et Oracle compris ; voir [Sources d’identité (LDAP et OIDC)](/fr/guide/identity-sources/). +- Les sources d’identité OpenID Connect peuvent synchroniser le numéro de téléphone et l’avatar et ajouter des paramètres à la requête d’autorisation (comme `provider_hint` pour Casdoor) ; les paramètres propres au protocole ne peuvent pas être remplacés. + +## Autorisation et catalogue des ressources + +- Autorisations directes des utilisateurs : autorisent pour un seul utilisateur des menus, pages, boutons ou API d’une application métier, éventuellement jusqu’à une date d’expiration ; elles se cumulent avec les autorisations des rôles, et un refus venant d’un rôle reste prioritaire ; voir [Utilisateurs](/fr/guide/users/). +- Le catalogue des ressources peut exporter l’arbre des ressources d’une application en JSON et l’importer dans une autre application : l’import est d’abord vérifié, et rien n’est appliqué en cas de conflit ; les administrateurs de la plateforme peuvent vider les arbres d’application éligibles ; voir [Catalogue des ressources et des API](/fr/guide/catalog/). + +## Serveur + +- Au démarrage, le serveur affiche une bannière GrantForge avec sa version et celle de Java au lieu de la bannière de Spring Boot. + +## Documentation et publication + +- L’onglet des plug-ins s’ouvre sur une vue d’ensemble, regroupe les systèmes par nature dans une barre latérale repliable et indique pour chacun le mode d’application, la prise en charge des filtres de lignes et du masquage et les versions vérifiées ; tous les README présentent les composants dans le même ordre. +- La CI vérifie que chaque page est entièrement traduite dans chaque langue : mêmes paragraphes, titres, tableaux, blocs de code et liens que la page chinoise, aucun texte dans l’écriture d’une autre langue et rien de recopié de la page anglaise ; les 11 README subissent les mêmes vérifications par rapport au README anglais. Toutes les traductions manquantes repérées par la vérification ont été complétées. +- La vérification des versions tient à jour le badge de version de chaque README. +- Les workflows de publication de la documentation et des versions ne s’exécutent plus sur les forks. +- Le serveur `grantforge-server` n’est plus publié dans les dépôts Maven : il embarque tous les plug-ins et dépasse leur limite de taille ; il est livré dans l’archive de version et l’image. + +## Corrections + +- Changer le type d’une ressource dans l’éditeur de ressource efface le code de ressource qui ne convient plus. +- Le dernier bloc de code du guide d’intégration JavaScript n’avale plus le texte qui le suit, et l’adresse de la console sur les pages Docker et de démarrage rapide ne transforme plus le texte suivant en lien. +- Dépendances mises à niveau vers des versions qui corrigent des avis de sécurité : MariaDB Connector/J 3.5.11, OpenTelemetry 1.67.0, Guava 33.7.2, Snappy 1.1.10.9, ainsi que vue-i18n, glob et minimatch dans la console et le SDK. diff --git a/docs/content/fr/deploy/docker.md b/docs/content/fr/deploy/docker.md index 97dc5b4f..8f0bcde2 100644 --- a/docs/content/fr/deploy/docker.md +++ b/docs/content/fr/deploy/docker.md @@ -43,7 +43,7 @@ docker compose -f deploy/compose/postgres.yml up -d docker compose -f deploy/compose/postgres.yml logs grantforge | grep "setup token" ``` -Ouvrez ensuite http://127.0.0.1:9999/. Le mot de passe par défaut de la base utilisé par l’exemple ne convient que pour un essai : modifiez-le via `GRANTFORGE_DB_PASSWORD` avant tout usage réel. L’exemple MySQL exige de déposer au préalable `mysql-connector-j-.jar` dans `deploy/compose/drivers/`. +Ouvrez ensuite . Le mot de passe par défaut de la base utilisé par l’exemple ne convient que pour un essai : modifiez-le via `GRANTFORGE_DB_PASSWORD` avant tout usage réel. L’exemple MySQL exige de déposer au préalable `mysql-connector-j-.jar` dans `deploy/compose/drivers/`. ## Helm diff --git a/docs/content/fr/develop/development.md b/docs/content/fr/develop/development.md index fa73beeb..697f0eeb 100644 --- a/docs/content/fr/develop/development.md +++ b/docs/content/fr/develop/development.md @@ -76,7 +76,7 @@ Le script exige un espace de travail propre, une branche locale qui n’est pas - `script/ci/release.sh` construit la version publiée, un SBOM CycloneDX ne contenant que les dépendances de publication et `SHA256SUMS` ; - les images multi-architectures sont poussées vers `ghcr.io/devlive-community/grantforge` ; -- les artefacts Maven (paquet de sources et Javadoc compris) sont publiés sur GitHub Packages ; lorsque le dépôt définit `CENTRAL_USERNAME`, `CENTRAL_PASSWORD` (jeton du Central Portal), `GPG_PRIVATE_KEY` et `GPG_PASSPHRASE`, ils sont signés puis publiés sur Maven Central ; +- les artefacts Maven (paquet de sources et Javadoc compris) sont publiés sur GitHub Packages ; lorsque le dépôt définit `CENTRAL_USERNAME`, `CENTRAL_PASSWORD` (jeton du Central Portal), `GPG_PRIVATE_KEY` et `GPG_PASSPHRASE`, ils sont signés puis publiés sur Maven Central ; le serveur `grantforge-server` embarque tous les plug-ins et dépasse la taille admise par les dépôts : il n’est publié dans aucun dépôt Maven et n’est livré que dans l’archive de version et l’image ; - une GitHub Release est créée, dont le corps reprend tous les commits depuis la version publiée précédente (`v*` ou un tag numérique comme `1.0.6`), regroupés par nouvelles fonctionnalités, corrections de bugs, performances, etc., avec des liens vers les commits. Les versions candidates sont marquées comme pré-publication et ne mettent pas à jour le tag `latest` des images. En local, l’activation du profil `central` ne publie rien par défaut (`central.skip=true`) ; seule la transmission explicite de `-Dcentral.skip=false` par le workflow de publication le fait. @@ -100,3 +100,5 @@ pnpm build # sortie vers docs/out ``` Les pages sont du Markdown situé sous `docs/content/`, et la navigation se trouve dans `docs/lib/navigation.ts`. La référence de l’API et les codes d’erreur sont générés à la construction à partir du contrat et du code source. Les captures d’écran sont produites par `script/docs/screenshots.sh`, qui démarre un vrai service, écrit des données d’exemple, puis fait appel à Playwright. + +`pnpm test` vérifie aussi les traductions : chaque page chinoise doit être traduite dans chacune des dix autres langues, avec les mêmes paragraphes, niveaux de titre, listes, tableaux, blocs de code et liens que la page chinoise. Une page ne doit contenir ni texte dans une écriture que sa langue n’utilise pas (y compris des caractères simplifiés en chinois traditionnel et en japonais), ni phrase recopiée telle quelle de la page anglaise. Les 11 README à la racine du dépôt subissent les mêmes vérifications par rapport au README anglais, et leurs liens vers ce site doivent pointer vers des pages existantes de leur propre langue. diff --git a/docs/content/fr/external/hdfs-agent.md b/docs/content/fr/external/hdfs-agent.md index e0f3c8f2..8f4e3ead 100644 --- a/docs/content/fr/external/hdfs-agent.md +++ b/docs/content/fr/external/hdfs-agent.md @@ -9,71 +9,69 @@ description: Appliquez les politiques GrantForge avec l’agent NameNode adapté project root for full license text. --> -Le plug-in serveur `grantforge-plugin-hdfs` définit les ressources et les connexions. Les agents NameNode numérotés pour Hadoop 2.7, 2.10, 3.2, 3.3, 3.4 et 3.5 contrôlent les accès via la SPI de leur version et partagent snapshots signés, évaluation des politiques et remontée d’audit. +Le plug-in serveur `grantforge-plugin-hdfs` fournit de façon unifiée le type de service `hdfs`, ses ressources et ses réglages de connexion. Les agents NameNode sont construits séparément pour chaque version de Hadoop, contrôlent les accès via les `INodeAttributeProvider` et `AccessControlEnforcer` de leur version et partagent la logique des politiques signées, du cache, de l’évaluation des politiques et de l’audit. -La logique HDFS commune se trouve dans `agents/grantforge-agent-hdfs-common`, les adaptateurs par version dans `agents/grantforge-agent-hdfs-`. Les adaptateurs natifs communs constituent le module Maven de production `agents/grantforge-agent-hdfs-native`, basé sur Java 8 / Hadoop 2.7.7. Chaque module numéroté utilise sa dépendance Maven binaire et conserve ses points d’entrée et callbacks propres à sa version, sans recompiler les sources de production communes. `core/grantforge-agent-core` reste la bibliothèque commune de protocole et d’exécution. +Dans les sources, le plug-in serveur se trouve dans `plugins/grantforge-plugin-hdfs`, les agents de version dans `agents/grantforge-agent-hdfs-` et la logique de production commune sans dépendance à Hadoop dans `agents/grantforge-agent-hdfs-common`. L’adaptation native partagée se trouve dans le module de production Maven `agents/grantforge-agent-hdfs-native`, avec Java 8 / Hadoop 2.7.7 comme base ; chaque module numéroté le référence comme dépendance binaire Maven, conserve ses points d’entrée et rappels propres à sa version et ne recompile plus les sources de production partagées. Le protocole, le cache des instantanés et l’infrastructure d’audit restent dans `core/grantforge-agent-core`. -Le plug-in serveur conserve un seul type de service `hdfs` et une version de client indépendante des agents. Pour Hadoop 2.x, utilisez `webhdfs://namenode:50070`, ou une adresse `swebhdfs://` avec HTTPS ; Hadoop 3.x peut utiliser RPC `hdfs://` ou WebHDFS. Les tests en conteneurs couvrent WebHDFS sur 2.x et les deux protocoles sur 3.x. RPC sur 2.x reste non certifié ; le protocole configuré ne change jamais automatiquement. +Le plug-in serveur conserve un seul type `hdfs`, et la version du client est indépendante de celle de l’agent. La connexion et la recherche de chemins sous Hadoop 2.x utilisent `webhdfs://namenode:50070` (l’adresse `swebhdfs://` correspondante avec HTTPS) ; Hadoop 3.x accepte le RPC `hdfs://` ou WebHDFS. La matrice de conteneurs vérifie WebHDFS en 2.x, ainsi que RPC et WebHDFS en 3.x ; le RPC 2.x n’est pas certifié et le protocole de connexion n’est jamais changé automatiquement. -Avec les extensions des attributs inode activées, Apache Hadoop 2.7.7 produit une erreur native de pointeur nul lorsqu’un utilisateur ordinaire interroge `/`, avant le rappel de l’agent. Utilisez des répertoires de données comme `/data` pour les opérations et `lookup.path` avec cette version. Le test en conteneur vérifie explicitement cette limitation. +Avec l’extension des attributs d’inode activée, Apache Hadoop 2.7.7 déclenche dans son code natif une erreur de pointeur nul lorsqu’un utilisateur ordinaire interroge directement la racine `/`, avant même le rappel de l’agent. Avec cette version, utilisez des répertoires de données réels comme `/data` pour les opérations sur les données et le `lookup.path` du service ; les tests conservent une assertion distincte de l’échec sur la racine. -## Versions cibles des agents HDFS +## Choisir la version -| Base Hadoop | Java du conteneur | Répertoire de l’agent | -| --- | --- | --- | -| 2.7.7 | Java 8 | `agents/hdfs/2.7/` | -| 2.10.2 | Java 8 | `agents/hdfs/2.10/` | -| 3.2.4 | Java 8 | `agents/hdfs/3.2/` | -| 3.3.6 | Java 8 (image amd64) | `agents/hdfs/3.3/` | -| 3.4.3 | Java 11 | `agents/hdfs/3.4/` | -| 3.5.0 | Java 17 | `agents/hdfs/3.5/` | - -Choisissez `grantforge-agent-hdfs--.jar` dans `agents/hdfs//` pour la branche Hadoop du cluster. La logique commune cible Java 8, l’adaptateur 3.5 Java 17. +| Suffixe du module d’agent | Apache Hadoop vérifié | JVM du conteneur | Interface d’autorisation | Rappel des chemins superutilisateur | +| --- | --- | --- | --- | --- | +| `2.7` | 2.7.7 | Java 8 | par paramètres | non | +| `2.10` | 2.10.2 | Java 8 | par paramètres | non | +| `3.2` | 3.2.4 | Java 8 | par paramètres | non | +| `3.3` | 3.3.6 | Java 8 | par contexte | non | +| `3.4` | 3.4.3 | Java 11 | par contexte, rappels superutilisateur et de refus | oui | +| `3.5` | 3.5.0 | Java 17 | par contexte, rappels superutilisateur et de refus | oui | -Hadoop 2.7, 2.10, 3.2 et 3.3 ne fournissent pas le rappel d’autorisation des superutilisateurs utilisé par l’agent. Hadoop garde le contrôle de ces accès ; utilisez des utilisateurs ordinaires pour les données soumises aux politiques GrantForge. +Pour Hadoop 2.10.2, par exemple, installez `agents/hdfs/2.10/grantforge-agent-hdfs-2.10-.jar`. Installez un seul agent de version par NameNode et retirez l’ancien jar lors d’une mise à niveau ; le nom de classe du provider dans la configuration ne change pas. Au démarrage, l’agent vérifie les versions majeure et mineure de Hadoop ainsi que les rappels requis, et refuse de démarrer si la version ne correspond pas ; les autres correctifs de la même version et les distributions des éditeurs doivent toujours être vérifiés séparément. -## Relation aux permissions natives +Le code principal des agents `common` et `2.7` à `3.4` utilise le bytecode Java 8, `3.5` Java 17. Le tableau indique les JVM réellement utilisées par les tests d’intégration ; il ne signifie pas que 3.4.3 a été vérifié sous Java 8. L’image de test officielle de 3.3.6 n’existe que pour amd64 : sur un hôte ARM, elle s’exécute explicitement en émulation amd64. -Le comportement suivant concerne les accès des utilisateurs ordinaires. Les limites des superutilisateurs figurent ci-dessus et dans la section sur leurs rappels. +## Relation avec les autorisations natives -L’agent exécute d’abord les vérifications de permissions natives HDFS, puis les politiques GrantForge : l’utilisateur doit satisfaire les deux. Les politiques d’autorisation de GrantForge ne contournent ni les permissions POSIX, ni les ACL, ni la vérification du propriétaire, ni le sticky bit ; les politiques de refus refusent toujours. Les réglages de permissions natives continuent d’être maintenus avec les outils d’administration de Hadoop. +L’agent exécute d’abord les contrôles d’autorisation natifs de HDFS, puis les politiques GrantForge : l’utilisateur doit satisfaire à la fois les autorisations natives et les politiques. Les politiques d’autorisation de GrantForge ne contournent ni les droits POSIX, ni les ACL, ni les contrôles de propriétaire, ni le sticky bit ; les politiques de refus refusent toujours. Les autorisations natives se gèrent toujours avec les outils d’administration de Hadoop. -Par défaut, `grantforge.hdfs.native.fallback=false` : l’accès aux données est refusé quand il n’y a pas d’instantané de politiques local, pas de politique correspondante, ou que l’agent n’a pas encore démarré. Avec `true`, les accès qu’aucune politique ne décide se rabattent sur les permissions natives ; les politiques de refus explicites restent en vigueur. Tant que le serveur est momentanément injoignable, le dernier instantané local ayant passé la vérification de signature continue d’être utilisé. +Par défaut, `grantforge.hdfs.native.fallback=false` : sans instantané local des politiques, sans politique correspondante ou tant que l’agent n’a pas démarré, l’accès aux données est refusé. Avec `true`, les accès qu’aucune politique ne tranche relèvent des autorisations natives ; les refus explicites restent en vigueur. Si le serveur est temporairement injoignable, le dernier instantané local à la signature vérifiée continue d’être utilisé. -Dans un même rappel d’autorisation, les ancêtres, la cible, les sous-arbres et les projections de chemin d’instantané utilisent une seule et même version de l’instantané de politiques ; les politiques rafraîchies prennent effet au rappel suivant, ce qui évite de combiner des règles d’autorisation de versions différentes. L’audit des accès consigne la version de politique réellement utilisée. +Les projections des ancêtres, de la cible, des sous-arbres et des chemins d’instantané au sein d’un même rappel d’autorisation utilisent la même version de l’instantané des politiques ; les politiques actualisées prennent effet au rappel suivant, ce qui évite de mélanger des règles d’autorisation de versions différentes. L’audit d’accès enregistre la version des politiques réellement utilisée. -L’agent vérifie les droits `read`, `write` et `execute` dont un utilisateur ordinaire a besoin pour la cible, ainsi que les répertoires parents, les répertoires ancêtres et les sous-répertoires à valider récursivement. Les opérations comme créer, supprimer ou renommer concernent plusieurs chemins, et les politiques d’autorisation doivent toutes les couvrir. En mode strict, une politique `read` sur le seul fichier cible ne suffit pas : il faut aussi configurer à l’utilisateur des politiques `execute` sur les répertoires ancêtres — par exemple autoriser `execute` sur `/` avec la récursion cochée, puis configurer lecture et écriture sur les répertoires de données réels. +L’agent vérifie les droits `read`, `write` et `execute` dont un utilisateur ordinaire a besoin sur la cible, ainsi que sur le répertoire parent, les répertoires ancêtres et les sous-répertoires à vérifier récursivement. Créer, supprimer, renommer et d’autres opérations touchent plusieurs chemins, et les politiques d’autorisation doivent tous les couvrir. En mode strict, une politique `read` sur le fichier cible ne suffit pas : l’utilisateur a aussi besoin de politiques `execute` sur les répertoires ancêtres, par exemple `execute` sur `/` avec la récursion cochée, puis de droits de lecture et d’écriture sur les répertoires de données réels. -Un chemin d’instantané est vérifié à la fois tel que demandé et sous sa forme d’origine, débarrassé de `.snapshot/` : par exemple `/data/.snapshot/s1/secret` est aussi vérifié en `/data/secret`. Les politiques de refus sur le chemin d’origine contraignent donc aussi les instantanés ; des restrictions plus strictes peuvent en outre être posées sur les chemins d’instantanés explicites. Les consultations de métadonnées suivent la sémantique de permission de parcours de répertoire de HDFS. +Les chemins d’instantané sont vérifiés à la fois tels qu’ils sont demandés et sous leur forme d’origine sans `.snapshot/` : `/data/.snapshot/s1/secret` vérifie aussi `/data/secret`. Les politiques de refus du chemin d’origine s’appliquent donc aussi aux instantanés ; on peut en plus poser des restrictions plus strictes sur les chemins d’instantané explicites. Les requêtes de métadonnées suivent la sémantique de parcours des répertoires de HDFS. -Une autorisation récursive vérifie au plus `100000` inodes ; au-delà de la limite, l’opération est refusée, afin d’éviter une allocation mémoire illimitée dans le NameNode. Les chemins très longs sont jugés avec le chemin complet ; l’affichage de la ressource d’audit est limité à `1000` caractères, et la longueur d’origine ainsi qu’un condensé SHA-256 sont consignés dans le détail de la requête. +Une autorisation récursive examine au plus `100000` inodes ; au-delà, l’opération est refusée pour éviter que le NameNode n’alloue de la mémoire sans limite. Les chemins très longs sont évalués sur le chemin complet ; l’affichage de la ressource dans l’audit est limité à `1000` caractères, et le détail de la requête enregistre la longueur d’origine et un condensat SHA-256. -Les rappels de superutilisateur suivants concernent Hadoop 3.4 et 3.5. Les superutilisateurs HDFS restent gérés par Hadoop. Un rappel de superutilisateur portant un chemin passe d’abord la vérification de superutilisateur de Hadoop, puis le contrôle des politiques selon le nom d’opération fourni par Hadoop 3.4 / 3.5 : lecture de fichier et consultation de métadonnées exigent `read`, l’énumération d’un répertoire exige `read` + `execute`, et les opérations de modification connues exigent `write`. Pour les opérations inconnues, absentes ou impossibles à déduire de façon fiable (par exemple `checkAccess` et `concat`), les trois permissions sont exigées par prudence. +Hadoop 2.7, 2.10, 3.2 et 3.3 laissent passer les superutilisateurs avant d’appeler l’agent, qui ne peut donc ni contrôler ni auditer ces accès. Avec Hadoop 3.4 et 3.5, les rappels superutilisateur avec chemin passent d’abord le contrôle natif, puis les politiques sont vérifiées selon le nom de l’opération : la lecture de fichiers et les requêtes de métadonnées exigent `read`, le listage de répertoires `read` + `execute`, et les opérations de modification connues `write`. Les opérations inconnues, absentes ou impossibles à déduire avec certitude (comme `checkAccess` et `concat`) exigent par prudence les trois droits. -Les rappels de superutilisateur n’ont pas le contexte complet des inodes et des sous-arbres, et les appels d’administration du cluster sans chemin conservent les vérifications natives ; il est impossible de restreindre avec des politiques de sous-répertoires toutes les opérations récursives d’un superutilisateur. Les utilisateurs de données doivent utiliser des utilisateurs Hadoop ordinaires. +Les rappels superutilisateur n’ont pas le contexte complet des inodes et des sous-arbres ; les appels d’administration du cluster sans chemin restent soumis au seul contrôle natif, et les politiques de sous-répertoires ne peuvent pas restreindre toutes les opérations récursives d’un superutilisateur. Les consommateurs de données doivent utiliser des utilisateurs Hadoop ordinaires. -## Indicateurs +## Métriques -L’agent remonte ses indicateurs par le système Metrics2 de Hadoop, sur les mêmes sinks que les indicateurs dfs du NameNode lui-même ; dans le JMX du NameNode, ils apparaissent sous `Hadoop:service=NameNode,name=GrantForgeHdfsAgent` (un exportateur JMX Prometheus peut les collecter directement). Chaque indicateur porte les étiquettes `instance` (nom de l’instance du service de données) et `agentVersion`, ce qui permet de distinguer les deux NameNodes d’une paire HA. Un échec d’enregistrement des indicateurs ne coûte que les indicateurs eux-mêmes : l’agent consigne un avertissement et continue d’appliquer les autorisations sans eux. +L’agent publie ses métriques via le système Metrics2 de Hadoop, avec les mêmes sinks que les métriques dfs du NameNode ; dans le JMX du NameNode, elles apparaissent sous `Hadoop:service=NameNode,name=GrantForgeHdfsAgent` (l’exportateur JMX de Prometheus peut les collecter directement). Chaque métrique porte les étiquettes `instance` (nom de l’instance du service de données) et `agentVersion`, ce qui permet de suivre séparément les deux NameNode d’une paire HA. Un échec d’enregistrement ne fait perdre que les métriques : l’agent journalise un avertissement et continue d’autoriser sans métriques. -| Indicateur | Description | +| Métrique | Description | | --- | --- | -| `Callbacks` | Nombre de rappels d’autorisation exécutés par l’agent | -| `SuperuserCallbacks` | Nombre de rappels de superutilisateur exécutés par l’agent (3.4 / 3.5) | -| `NativeDenies` | Nombre d’accès refusés par Hadoop avant même l’exécution de l’agent | -| `EvaluationFailures` | Nombre de rappels fermés par refus à cause d’une exception d’évaluation des politiques | -| `DecisionsAllowed` / `DecisionsDenied` / `DecisionsUndetermined` | Nombre de droits jugés autorisés / refusés / indécis par les politiques ; l’indécis est également refusé en mode strict | -| `MissingSnapshots` | Nombre de rappels servis alors qu’aucun instantané de politiques vérifié n’existait | -| `SnapshotVersion` | Version de l’instantané de politiques en cours d’utilisation, 0 quand il n’y en a pas | -| `QueuedEvents` / `DroppedEvents` | Nombre d’événements d’audit en attente en mémoire / d’événements abandonnés parce que la file ou le tampon disque était plein | +| `Callbacks` | Rappels d’autorisation exécutés par l’agent | +| `SuperuserCallbacks` | Rappels superutilisateur exécutés par l’agent | +| `NativeDenies` | Accès refusés par Hadoop avant l’agent | +| `EvaluationFailures` | Rappels fermés en échec à cause d’une exception d’évaluation des politiques | +| `DecisionsAllowed` / `DecisionsDenied` / `DecisionsUndetermined` | Droits que les politiques ont autorisés / refusés / laissés indéterminés ; les indéterminés sont aussi refusés en mode strict | +| `MissingSnapshots` | Rappels servis sans instantané des politiques vérifié | +| `SnapshotVersion` | Version de l’instantané des politiques en cours, 0 s’il n’y en a pas | +| `QueuedEvents` / `DroppedEvents` | Événements d’audit en attente d’envoi en mémoire / abandonnés parce que la file ou le tampon disque était plein | | `ServerReachable` | Si le dernier accès au serveur de politiques a réussi (1/0) | ## Déploiement -1. Ajoutez un service `hdfs` dans les services de données de GrantForge, enregistrez la configuration et testez la connexion ; configurez des politiques de chemin pour les noms courts d’utilisateur, groupes ou rôles Hadoop réels. -2. Dans « Autorisations sur les données → Agents », émettez un jeton pour ce service. Écrivez le jeton en clair dans un fichier local de chaque NameNode, par exemple `/etc/hadoop/grantforge/token`, lisible par l’utilisateur qui exécute le NameNode. -3. Sélectionnez la branche Hadoop du cluster et copiez `grantforge-agent-hdfs--.jar` depuis `agents/hdfs//` dans le classpath du NameNode, par exemple `$HADOOP_HOME/share/hadoop/hdfs/lib/`. Installez un seul adaptateur compatible. Le jar contient la logique commune, Jackson et la bibliothèque de signature ; Hadoop est fourni par le NameNode. -4. Configurez les propriétés suivantes dans le `hdfs-site.xml` de chaque NameNode ; les deux NameNodes d’une installation HA utilisent des `instance` distincts et leurs propres répertoires de cache locaux. +1. Ajoutez un service `hdfs` dans les services de données de GrantForge, enregistrez la configuration et testez la connexion ; configurez des politiques de chemin pour les noms courts réels des utilisateurs Hadoop, leurs groupes ou leurs rôles. +2. Dans « Autorisations sur les données → Agents », émettez un jeton pour ce service. Écrivez le texte du jeton dans un fichier local sur chaque NameNode, par exemple `/etc/hadoop/grantforge/token`, lisible par l’utilisateur qui exécute le NameNode. +3. Selon le tableau, choisissez le jar d’agent correspondant dans `agents/hdfs//` du paquet de distribution et placez-le dans le classpath du NameNode, par exemple `$HADOOP_HOME/share/hadoop/hdfs/lib/`. Le jar d’agent embarque son propre moteur de politiques, Jackson et la bibliothèque de signature ; les classes Hadoop sont fournies par le NameNode. La version d’agent envoyée dans le heartbeat comprend la version du produit et la version de Hadoop utilisée pour la compilation. +4. Configurez les propriétés suivantes dans le `hdfs-site.xml` de chaque NameNode ; les deux NameNode d’une paire HA utilisent des `instance` différentes et chacun son propre répertoire de cache local. ```xml @@ -102,58 +100,56 @@ L’agent remonte ses indicateurs par le système Metrics2 de Hadoop, sur les m ``` -5. Confirmez que `dfs.permissions.enabled=true` et que `dfs.namenode.inode.attributes.provider.bypass.users` est vide ; au démarrage, l’agent refuse toute configuration qui permettrait de contourner les rappels d’autorisation. Redémarrez le NameNode, puis vérifiez le battement de cœur et la version de politique sur la page des agents de GrantForge. L’agent lit la configuration existante du NameNode ; il ne modifie pas les attributs natifs des inodes. +5. Vérifiez que `dfs.permissions.enabled=true` et que `dfs.namenode.inode.attributes.provider.bypass.users` est vide ; l’agent refuse de démarrer avec une configuration qui permettrait de contourner le rappel d’autorisation. Redémarrez le NameNode, puis vérifiez le heartbeat et la version des politiques sur la page des agents de GrantForge. L’agent lit la configuration existante du NameNode et ne modifie pas les attributs natifs des inodes. -Pour un premier déploiement, vous pouvez commencer avec `native.fallback=true`, confirmer que l’instantané de politiques est synchronisé et que les permissions des répertoires ancêtres sont complètes, puis passer en mode strict. Le type de service auquel est lié le jeton doit être `hdfs` ; une erreur de configuration ou un lien à un autre type de service entraîne le refus des accès. +Pour un premier déploiement, vous pouvez commencer avec `native.fallback=true`, vérifier que l’instantané des politiques est synchronisé et compléter les droits sur les répertoires ancêtres, puis passer en mode strict. Le jeton doit être lié au type de service `hdfs` ; une configuration erronée ou un lien vers un autre type de service entraîne un refus. ## Réglages facultatifs -| Propriété | Valeur par défaut | Usage | +| Propriété | Valeur par défaut | Rôle | | --- | --- | --- | -| `grantforge.hdfs.connect.timeout.ms` | `5000` | Délai d’expiration de la connexion à GrantForge | -| `grantforge.hdfs.read.timeout.ms` | `8000` | Délai d’expiration de lecture de la réponse | -| `grantforge.hdfs.refresh.interval.ms` | `30000` | Intervalle de rafraîchissement des politiques quand le serveur est injoignable, au moins `1000` ; les battements de cœur normaux utilisent l’intervalle conseillé par le serveur | -| `grantforge.hdfs.signing.key.file` | non défini | Fichier facultatif de clé publique de signature, contenant la clé publique X.509 Base64 fournie par la console ; une fois configuré, seules les signatures de cette clé sont acceptées | -| `grantforge.hdfs.audit.batch.size` | `500` | Nombre maximal d’événements par remontée, plage `1..1000` | -| `grantforge.hdfs.audit.queue.capacity` | `10000` | Capacité de la file d’audit en mémoire, plage `1..1000000`, pouvant contenir au moins un lot ; quand la file est pleine, les nouveaux événements sont comptés puis abandonnés | -| `grantforge.hdfs.audit.flush.interval.ms` | `5000` | Intervalle de vidange de l’audit, entier positif, au plus `2147483647` ; le réduire diminue la latence de remontée | -| `grantforge.hdfs.audit.spool.limit.bytes` | `67108864` | Limite du tampon disque quand le serveur est injoignable, entier non négatif ; `0` désactive le tampon disque | - -En cas de pics d’accès, vous pouvez agrandir la file d’audit pour réduire les débordements ; raccourcir l’intervalle de vidange diminue la latence d’audit mais augmente la fréquence de remontée. La limite du tampon disque sert à maîtriser l’espace occupé lors d’une coupure prolongée ; désactivé ou épuisé, le tampon peut laisser perdre des événements. La remontée d’audit s’exécute en arrière-plan et n’attend pas la réponse du serveur de politiques. +| `grantforge.hdfs.connect.timeout.ms` | `5000` | Délai de connexion à GrantForge | +| `grantforge.hdfs.read.timeout.ms` | `8000` | Délai de lecture de la réponse | +| `grantforge.hdfs.refresh.interval.ms` | `30000` | Intervalle d’actualisation des politiques tant que le serveur est injoignable, au moins `1000` ; le heartbeat normal suit l’intervalle conseillé par le serveur | +| `grantforge.hdfs.signing.key.file` | non défini | Fichier facultatif de clé publique de signature, contenant la clé publique X.509 en Base64 fournie par la console ; une fois défini, seules les signatures de cette clé sont acceptées | +| `grantforge.hdfs.audit.batch.size` | `500` | Nombre maximal d’événements par envoi, plage `1..1000` | +| `grantforge.hdfs.audit.queue.capacity` | `10000` | Capacité de la file d’audit en mémoire, plage `1..1000000`, au moins un lot ; quand elle est pleine, les nouveaux événements sont comptés et abandonnés | +| `grantforge.hdfs.audit.flush.interval.ms` | `5000` | Intervalle de vidage de l’audit, entier positif d’au plus `2147483647` ; une valeur plus petite réduit le délai d’envoi | +| `grantforge.hdfs.audit.spool.limit.bytes` | `67108864` | Limite du tampon disque tant que le serveur est injoignable, entier positif ou nul ; `0` désactive le tampon disque | -Sans clé publique de signature configurée, l’agent récupère la clé publique auprès du serveur au premier contact et l’enregistre avec l’instantané. L’agent utilise le nom d’utilisateur court et les groupes transmis par Hadoop ; les rôles et groupes supplémentaires viennent de l’instantané signé ; la correspondance des noms courts des principaux Kerberos est déterminée par le `hadoop.security.auth_to_local` du cluster. +En cas de pics d’accès, augmentez la file d’audit pour limiter les débordements ; raccourcir l’intervalle de vidage réduit le délai d’audit mais augmente la fréquence des envois. La limite du tampon disque borne l’espace disque utilisé lors de longues coupures ; tampon désactivé ou épuisé, des événements peuvent être perdus. L’audit est envoyé en arrière-plan sans attendre la réponse du serveur de politiques. -## Construire et vérifier depuis les sources +Sans clé publique de signature configurée, l’agent la récupère auprès du serveur au premier accès et l’enregistre avec l’instantané. L’agent utilise le nom d’utilisateur court et les groupes transmis par Hadoop ; les rôles et les groupes supplémentaires viennent de l’instantané signé. La correspondance entre principals Kerberos et noms courts est déterminée par le `hadoop.security.auth_to_local` du cluster. -Les commandes utilisent 3.5 ; remplacez cette branche par 2.7, 2.10, 3.2, 3.3 ou 3.4 pour l’adaptateur voulu. +## Construire depuis les sources et vérifier ```sh ./mvnw -pl agents/grantforge-agent-hdfs-3.5 -am package ./mvnw -pl plugins/grantforge-plugin-hdfs,agents/grantforge-agent-hdfs-3.5,core/grantforge-plugin-host -am test ``` -L’artefact de l’agent se trouve dans `agents/grantforge-agent-hdfs-3.5/target/grantforge-agent-hdfs-3.5-.jar`. Les tests unitaires couvrent les rappels d’autorisation du NameNode, la configuration, les métadonnées de version et les décisions de politique ; les tests WebHDFS et Kerberos démarrent des services locaux temporaires. Avant la mise en production, il faut en plus vérifier sur le cluster cible la lecture/écriture, la création, le renommage, la suppression récursive, le basculement HA et le comportement du cache après une coupure réseau. +L’artefact de l’agent se trouve dans `agents/grantforge-agent-hdfs-3.5/target/grantforge-agent-hdfs-3.5-.jar`. Les tests unitaires couvrent les rappels d’autorisation du NameNode, la configuration, les métadonnées de version et les décisions des politiques ; les tests WebHDFS et Kerberos démarrent des services locaux temporaires. Avant la mise en production, vérifiez aussi sur le cluster cible la lecture et l’écriture, la création, le renommage, la suppression récursive, la bascule HA et le comportement du cache après une coupure. -La vérification d’intégration s’exécute avec la phase `verify` via Testcontainers (les tests unitaires ne démarrent pas de cluster ; `verify` nécessite un démon Docker disponible) : +La vérification d’intégration s’exécute avec Testcontainers pendant la phase `verify` (les tests unitaires ne démarrent pas de cluster ; `verify` exige un démon Docker disponible) : ```sh ./mvnw -pl agents/grantforge-agent-hdfs-3.5 -am verify # même point d’entrée que nightly -bash script/ci/hdfs_integration.sh 3.5 -# Une branche ou la matrice complète -bash script/ci/hdfs_integration.sh all +bash script/ci/hdfs_integration.sh +# ne vérifier que la ligne de versions indiquée +bash script/ci/hdfs_integration.sh 2.10 ``` -Les tests utilisent des images de conteneur Apache Hadoop à version figée et placent le jar d’agent réellement empaqueté sur le classpath du NameNode. Testcontainers crée un réseau isolé et gère le cycle de vie des NameNode et DataNode, en vérifiant lecture/écriture, création, ajout, renommage, suppression, refus récursifs et sur les instantanés, permissions natives et audit, rafraîchissement des politiques, redémarrage du NameNode sur le cache signé après déconnexion du serveur de politiques, ainsi que le mode strict et le repli sur les permissions natives en l’absence d’instantané. +Les tests utilisent des images Apache Hadoop de version fixe ; les images anciennes sont construites à partir d’une image de base Java 8 au condensat fixé et d’une distribution Apache vérifiée par SHA-512. Le véritable jar d’agent de la version correspondante est placé dans le classpath du NameNode, et les versions réelles de Hadoop et de la JVM sont vérifiées. Testcontainers crée un réseau isolé et gère le cycle de vie du NameNode et du DataNode ; les tests vérifient la lecture et l’écriture, la création, l’ajout, le renommage, la suppression, les refus récursifs et sur instantané, les autorisations natives et l’audit, l’actualisation des politiques, le redémarrage du NameNode avec le cache signé après coupure du serveur de politiques, ainsi que le mode strict et le repli sur les autorisations natives en l’absence d’instantané. -Un démon Docker en fonctionnement est nécessaire, et le téléchargement des images de test doit être autorisé. Si Docker n’est pas disponible, les tests échouent ; ils ne sont jamais ignorés en silence. Le client de système de fichiers s’exécute à l’intérieur du conteneur Hadoop, et le service HTTP de politiques utilise la redirection de port hôte de Testcontainers ; aucun cluster Hadoop externe n’est requis. À la fin des tests, les conteneurs et le réseau de test sont nettoyés, et les journaux sont conservés dans `agents/grantforge-agent-hdfs-3.5/target/hdfs-testcontainers`. +Un démon Docker en marche est nécessaire, ainsi que l’autorisation de télécharger les images de test. Sans Docker, les tests échouent au lieu d’être ignorés en silence. Le client du système de fichiers s’exécute dans le conteneur Hadoop, et le service HTTP de politiques passe par la redirection de ports de l’hôte de Testcontainers ; aucun cluster Hadoop externe n’est nécessaire. Après les tests, les conteneurs et le réseau de test sont supprimés, et les journaux sont conservés dans `agents/grantforge-agent-hdfs-3.5/target/hdfs-testcontainers`. -Le test HA démarre deux NameNode, un DataNode et un JournalNode, et configure pour les deux agents des noms d’instance et répertoires de cache indépendants. Il utilise le client HDFS logique pour basculer manuellement le nœud actif, et vérifie la lecture/écriture et les politiques de refus après le basculement ; le JournalNode unique ne sert qu’aux tests : la tolérance de panne par quorum n’est pas vérifiée, et le basculement automatique via ZooKeeper n’est pas concerné. +Le test HA démarre deux NameNode, un DataNode et un JournalNode, et donne aux deux agents des noms d’instance et des répertoires de cache distincts. Il bascule le nœud actif à la main avec un client HDFS logique, puis vérifie la lecture, l’écriture et les politiques de refus après la bascule ; un seul JournalNode ne sert qu’au test, si bien que ni la tolérance aux pannes par majorité ni le basculement automatique via ZooKeeper ne sont vérifiés. -Le test de HA automatique (Hadoop 2.10 à 3.5, Java 17 et 21) démarre ZooKeeper, trois JournalNodes, deux NameNodes avec chacun son ZKFC et un DataNode. Il vérifie que, lorsque le NameNode actif est arrêté brutalement, ZooKeeper fait prendre le relais à l’autre, qui continue d’appliquer les stratégies et attribue les refus à sa propre instance ; que les stratégies publiées pendant l’absence d’un NameNode s’appliquent dès son retour en standby et restent appliquées après le retour du rôle, jamais une version plus ancienne ; que sans serveur de stratégies le NameNode qui prend le relais continue avec l’instantané qu’il détient et transmet son audit au retour du serveur ; et qu’avec un JournalNode sur trois arrêté les écritures réussissent, tandis qu’avec deux arrêtés l’écriture échoue et le NameNode actif s’arrête au lieu de continuer sans quorum. Une variante Kerberos, sur Hadoop 3.5.0 uniquement, ajoute des JournalNodes qui se connectent par keytab et ne servent que HTTPS, des ZKFC qui s’authentifient auprès de ZooKeeper par SASL, et des znodes d’élection réservés au principal des NameNodes (un client ZooKeeper non authentifié ne peut même pas les lire) ; elle vérifie que l’autre NameNode prend le relais quand l’actif est arrêté et continue d’appliquer les stratégies aux utilisateurs Kerberos, qu’un client sans ticket est toujours refusé et que le rôle revient au premier NameNode à son retour. Les JournalNodes tournent sous un principal portant le nom court des NameNodes, car un JournalNode ne remet les edits qu’au principal complet d’un NameNode ou à un demandeur ayant son propre nom court, et l’authentification HTTP ne lui transmet que des noms courts. La HA automatique sur Hadoop 2.7 n’est pas vérifiée : son client ne bascule que tant que le nom d’hôte de chaque NameNode se résout, or celui d’un conteneur arrêté ne se résout plus ; il abandonne alors avec UnknownHostException au lieu d’essayer l’autre NameNode. +Le test de HA automatique (Hadoop 2.10 à 3.5, Java 17 et 21) démarre ZooKeeper, trois JournalNode, deux NameNode dotés chacun d’un ZKFC et un DataNode. Il vérifie qu’après l’arrêt direct du NameNode actif, ZooKeeper élit un autre nœud, que le nouveau nœud actif continue d’appliquer les politiques et que les événements de refus sont rattachés à son instance ; que les nouvelles politiques publiées pendant l’absence d’un nœud s’appliquent après son retour en Standby, et qu’après la bascule inverse la nouvelle version reste appliquée au lieu de revenir à l’ancienne ; que, tant que le service de politiques est indisponible, le nœud qui a pris le relais continue avec l’instantané déjà chargé et renvoie l’audit une fois le service rétabli ; qu’avec un JournalNode sur trois arrêté les écritures réussissent, alors qu’avec deux elles échouent et le NameNode actif s’arrête au lieu de continuer sans majorité. Il existe aussi une variante Kerberos (Hadoop 3.5.0 uniquement) : les JournalNode se connectent aussi par keytab et n’ouvrent que HTTPS, les ZKFC s’authentifient auprès de ZooKeeper par SASL, et seul le principal du NameNode peut accéder au nœud d’élection (un client ZooKeeper non authentifié ne peut même pas lire) ; le test vérifie qu’après l’arrêt du NameNode actif un autre nœud prend le relais et continue d’appliquer les politiques aux utilisateurs Kerberos, que les clients sans ticket sont toujours refusés et qu’on peut rebasculer une fois le nœud d’origine revenu. Les JournalNode tournent sous un principal de même nom court que celui du NameNode, car un JournalNode ne livre les edits qu’au principal complet du NameNode ou à un demandeur portant son propre nom court, et l’authentification HTTP ne fournit que le nom court. Le HA automatique de Hadoop 2.7 n’est pas vérifié : son client ne bascule que si tous les noms d’hôte des NameNode se résolvent ; le nom du conteneur arrêté pendant le test ne se résout plus, si bien que le client signale aussitôt une UnknownHostException sans essayer l’autre nœud. -Le test Kerberos s’exécute sur les six lignes (Java 17 et 21) : le KDC tourne dans la JVM de test, le NameNode et le DataNode démarrent en mode sécurisé avec leurs propres keytabs, le DataNode ne transfère des données qu’après SASL et ne sert que HTTPS, et WebHDFS utilise SPNEGO. Il vérifie que les stratégies GrantForge s’appliquent aux lectures et écritures une fois les principals associés à des noms courts, qu’un utilisateur sans stratégie est refusé, que les refus sont audités sous le nom court, qu’un client sans ticket est refusé sans retomber sur l’authentification simple, et qu’un NameNode redémarré se reconnecte et continue d’appliquer les stratégies. +Le test Kerberos s’exécute sur les six lignes de versions (Java 17 et 21) : le KDC tourne dans la JVM de test, le NameNode et le DataNode démarrent en mode sécurisé avec chacun son keytab, le transfert de données du DataNode exige SASL et n’ouvre que HTTPS, et WebHDFS utilise SPNEGO. Le test vérifie qu’une fois le principal ramené au nom court, les politiques GrantForge s’appliquent à la lecture et à l’écriture, que les utilisateurs sans politique sont refusés, que les refus sont audités sous le nom court, que les clients sans ticket sont refusés sans repli sur l’authentification simple, et que le NameNode se reconnecte après un redémarrage et continue d’appliquer les politiques. -Les tests unitaires natifs communs s’exécutent dans `agents/grantforge-agent-hdfs-native/src/test`, sans être compilés dans les six adaptateurs. Les tests des callbacks propres à chaque version restent dans leurs modules numérotés. Les sources communes des tests de conteneurs dans `agents/grantforge-agent-hdfs-common/src/test/shared` sont toujours compilées dans les modules de production numérotés. Aucun projet Maven réservé aux tests n’est créé ; Testcontainers reste une dépendance de test. Nightly teste les six versions Hadoop sur des hôtes Java 17/21 ; la JVM de chaque conteneur suit la matrice ci-dessus. Les rapports et logs sont conservés. +Les sources des tests se trouvent dans le `src/test` des véritables modules de production. Les tests unitaires communs des politiques s’exécutent dans common et les tests unitaires natifs partagés dans `agents/grantforge-agent-hdfs-native/src/test` ; ils ne sont plus compilés dans les six agents de version. Les tests de rappel de chaque version restent dans leur module numéroté, et les sources de tests de conteneur partagées `agents/grantforge-agent-hdfs-common/src/test/shared` sont toujours compilées dans chaque module numéroté ; il n’y a pas de projet Maven de test séparé. Testcontainers n’est présent qu’avec la portée test. Nightly vérifie les six versions de Hadoop sur des hôtes Java 17 et 21, et les conteneurs Hadoop utilisent les JVM du tableau. Les conteneurs couvrent aujourd’hui l’authentification Simple, la HA manuelle, Kerberos et HTTPS sur les six lignes de versions, la HA automatique de 2.10 à 3.5 et la HA automatique sous Kerberos sur Hadoop 3.5.0 ; la HA automatique de 2.7, la HA automatique sous Kerberos sur les autres versions et les correctifs des éditeurs restent à vérifier dans l’environnement cible. -Pour les points d’entrée d’extension de Hadoop et la sémantique des permissions, voir l’[API Apache Hadoop 3.5.0](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/build/source/hadoop-hdfs-project/hadoop-hdfs/target/api/org/apache/hadoop/hdfs/server/namenode/INodeAttributeProvider.html) et le [guide des permissions HDFS](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/HdfsPermissionsGuide.html). +Les points d’extension et la sémantique des autorisations de Hadoop sont décrits dans l’[API Apache Hadoop 3.5.0](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/build/source/hadoop-hdfs-project/hadoop-hdfs/target/api/org/apache/hadoop/hdfs/server/namenode/INodeAttributeProvider.html) et le [guide des autorisations HDFS](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/HdfsPermissionsGuide.html). diff --git a/docs/content/fr/guide/catalog.md b/docs/content/fr/guide/catalog.md index 8b949bd0..d570bd40 100644 --- a/docs/content/fr/guide/catalog.md +++ b/docs/content/fr/guide/catalog.md @@ -22,6 +22,12 @@ description: Maintenir les applications et l’arborescence des ressources, les - **Clients OAuth** : enregistrez des clients pour les applications métier ; voir [OAuth 2.1 et OpenID Connect](/fr/integration/oauth/). - **Champs** : lorsqu’un champ est sélectionné, les interfaces dans lesquelles il apparaît (en retour ou en réception) sont affichées. +Dans le catalogue d’une application qui a des ressources, cliquez sur **Exporter** pour télécharger un fichier JSON destiné au développement ou aux outils d’IA. Il contient les informations de l’application, l’ensemble des ressources organisées par hiérarchie et leurs dépendances, avec les codes des ressources aux deux extrémités ; la recherche et les filtres n’influent pas sur l’export. `formatVersion` indique la version du format d’export. + +Choisissez l’application cible, cliquez sur **Import**, téléversez un fichier d’export de version 1 et lancez d’abord la vérification. L’import réutilise une ressource de même code dans la même application seulement si son type et son parent correspondent, sans écraser les réglages existants ; un conflit de type, de parent ou de nature de dépendance annule tout l’import. Les nouvelles ressources et dépendances sont écrites ensemble après confirmation, et répéter un import ne crée pas de doublons. Les applications intégrées ne peuvent pas recevoir d’import ; les dépendances déclarées deviennent manuelles après l’import. Importer des ressources exige le droit de créer des ressources, et importer des dépendances exige en plus celui de les modifier. + +L’export ne contient que ce qui est enregistré dans GrantForge : `route` des pages et ressources similaires est la route du frontend, `code` des ressources API est le code d’autorisation. Les méthodes HTTP et chemins des API métier externes ne sont pas déduits s’ils ne sont pas enregistrés. Le JSON est un instantané au moment de l’export, sans les autorisations d’aucun utilisateur ; exportez à nouveau après une modification des ressources. + ## Catalogue d’API **Administration de la plateforme → Catalogue d’API** liste toutes les interfaces enregistrées automatiquement au démarrage du serveur et leurs exigences d’accès : public, simple compte connecté, ou code de permission requis. Les interfaces nécessitant une autorisation sont rattachées au catalogue de ressources selon leur code de permission, et les rôles y font référence lors de l’attribution des autorisations. Lorsqu’une interface est ajoutée, retirée, ou que son code de permission change, elle apparaît dans les « modifications à confirmer » ; le catalogue est mis à jour après confirmation. diff --git a/docs/content/fr/guide/explain.md b/docs/content/fr/guide/explain.md index ce129463..12b7f5d8 100644 --- a/docs/content/fr/guide/explain.md +++ b/docs/content/fr/guide/explain.md @@ -13,6 +13,8 @@ description: Consultez ce qu’une personne peut utiliser et pourquoi, simulez d Dans **Gestion des utilisateurs**, cliquez sur « Voir les autorisations effectives » dans la ligne d’un utilisateur pour voir tout ce que cet utilisateur peut utiliser actuellement : rôles, menus et boutons, API, portée des données et champs restreints. +Choisissez une application pour voir ses ressources et autorisations API effectives ; « Pourquoi » indique si elles viennent des rôles ou d’autorisations directes de l’utilisateur. Les rôles, périmètres de données et autorisations de champ portent sur tout le compte. Lors d’une simulation d’ajout ou de retrait de rôles, les autorisations directes existantes sont conservées. + ![Autorisations effectives](/screenshots/user-permissions.png) Cliquez sur **Expliquer** sur n’importe quel élément : GrantForge répond à la question « pourquoi cela fonctionne-t-il » en indiquant par quel rôle, à travers quelles attributions (directe, groupe d’utilisateurs, service, poste) et quelles ressources déduites il l’obtient ; lorsque cela ne fonctionne pas, il précise s’il n’y a pas d’autorisation ou si elle est bloquée par une règle de refus. diff --git a/docs/content/fr/guide/identity-sources.md b/docs/content/fr/guide/identity-sources.md index b89fe915..a989edac 100644 --- a/docs/content/fr/guide/identity-sources.md +++ b/docs/content/fr/guide/identity-sources.md @@ -44,6 +44,11 @@ Choisissez le type « OpenID Connect » pour intégrer Keycloak, Azure AD, Okta | ID et secret du client | client enregistré auprès du fournisseur ; sans secret, il fonctionne comme client public avec PKCE | | Adresse de rappel | l’adresse `/api/v1/auth/federated/callback/` affichée sur la page, à enregistrer dans le client côté fournisseur | | Scopes et revendications | `openid profile email` par défaut ; le nom d’utilisateur, le nom d’affichage et l’e-mail sont lus respectivement dans les revendications `preferred_username`, `name` et `email` | +| Revendication du téléphone | lit `phone` par défaut (Casdoor) ; pour d’autres fournisseurs, indiquez `phone_number`. Pour synchroniser le numéro de téléphone, le scope doit inclure `phone`. Si le fournisseur ne renvoie aucun numéro à la connexion, le numéro existant est conservé | +| Adresse de l’avatar | synchronisée depuis la revendication OIDC standard `picture` ; le scope doit inclure `profile`. Sans valeur renvoyée, l’adresse existante est conservée | +| Paramètres supplémentaires de la requête d’autorisation | objet JSON facultatif dont les valeurs doivent être des chaînes. Avec Casdoor, par exemple, `{"provider_hint":"github"}` désigne un fournisseur configuré. Les paramètres apparaissent dans l’adresse du navigateur : n’y mettez jamais de secret | + +Les paramètres essentiels du protocole (dont `state`, `nonce`, `redirect_uri` et ceux de PKCE) sont gérés par GrantForge ; les paramètres supplémentaires ne peuvent pas les remplacer. Une fois l’option activée, la page de connexion affiche un bouton « Se connecter avec ». L’utilisateur se connecte chez le fournisseur puis revient sur GrantForge ; les comptes ayant activé l’authentification à deux facteurs doivent en plus saisir un code. @@ -54,3 +59,26 @@ Une fois l’option activée, la page de connexion affiche un bouton « Se conne - Une source d’identité qui ne crée pas automatiquement de comptes ne permet de se connecter qu’aux utilisateurs déjà associés. - Après la désactivation d’une source d’identité, ses utilisateurs ne peuvent plus se connecter ; une source d’identité encore utilisée par des comptes ne peut pas être supprimée. - Les comptes issus d’une source d’identité peuvent recevoir des rôles et activer l’authentification à deux facteurs comme les autres. + +La synchronisation de l’organisation se configure dans une source de données distincte, sans modifier les réglages de connexion OIDC. + +## Sources de données et synchronisation de l’organisation + +Les sources de données synchronisent l’organisation, tandis que les sources d’identité continuent d’assurer la connexion OIDC ou LDAP. Les deux se configurent séparément : désactiver une source de données n’affecte pas la connexion OIDC. + +1. Configurez la connexion OpenID Connect existante dans « Contrôle d’accès → Sources d’identité ». +2. Dans « Contrôle d’accès → Sources de données », ajoutez une source DingTalk et choisissez la source d’identité OIDC associée. +3. Saisissez CorpId, AppKey, AppSecret et l’ID du département racine (généralement `1`). L’AppSecret est chiffré à part, l’API ne le renvoie jamais en clair, et un champ laissé vide lors d’une modification conserve la valeur existante. +4. La source peut être montée sous un département local. L’intervalle de synchronisation est de 30 minutes par défaut et peut aller de 15 à 10080 minutes ; un champ vide signifie une synchronisation manuelle uniquement. +5. Après l’enregistrement, testez la connexion et lancez la première synchronisation. L’application DingTalk doit pouvoir lire les départements et l’annuaire des employés, et son périmètre d’autorisation doit couvrir le sous-arbre choisi. +6. Quand un utilisateur se connecte par SSO, l’employé est retrouvé par l’adresse e-mail OIDC de confiance, et ses départements ainsi que son département principal sont renseignés automatiquement. L’adresse doit être gérée par un administrateur, ne pas permettre à un employé d’usurper une autre identité et correspondre à l’adresse DingTalk. Sans adresse d’employé, l’adresse d’entreprise est utilisée. + +La connexion lit uniquement l’instantané local de l’annuaire et n’interroge pas DingTalk. Avec la MFA, l’appartenance est mise à jour après la réussite de la seconde étape. Les comptes connectés avant la première synchronisation conservent l’adresse de confiance et sont associés une fois la synchronisation terminée ; l’adresse du profil, modifiable localement, ne sert jamais à la correspondance. Une adresse absente, en double ou déjà liée à un autre compte ne crée pas de nouvelle liaison. + +La liaison à un employé repose sur son ID externe et ne passe pas à un autre employé quand l’adresse change. La synchronisation gère l’appartenance à plusieurs départements ; le département principal reste l’ancien s’il est toujours valide, sinon il est choisi dans l’ordre stable des ID de département. Les départements locaux, groupes, postes et attributions directes de rôles sont conservés. Si un rôle est attribué à un département synchronisé, les utilisateurs en héritent selon les règles existantes, et une mutation met leurs autorisations à jour. + +Dès qu’un instantané de l’organisation ou des comptes en attente d’association existent, la source d’identité associée, l’entreprise, l’application, le département racine et le point de montage sont figés. Les départements et appartenances synchronisés sont en lecture seule et ne peuvent pas être remplacés à la main dans la gestion des utilisateurs ou de l’organisation. Les départements disparus sont conservés pour protéger les autorisations existantes ; quand un employé quitte l’annuaire autorisé, les appartenances gérées par cette source sont retirées sans que son compte soit désactivé automatiquement. Une source de données qui possède des départements ne peut pas être supprimée directement, mais elle peut être désactivée. + +La synchronisation récupère toutes les pages avant d’appliquer le résultat de façon atomique. En cas d’échec de lecture, de hiérarchie anormale ou de changement du périmètre d’autorisation, le dernier instantané réussi est conservé ; les administrateurs vérifient le résultat grâce au résumé de synchronisation, à la liste des anomalies et au journal d’audit. Si le périmètre a changé, rétablissez d’abord le périmètre approuvé, puis relancez la synchronisation. + +L’accès passe par le [SDK serveur officiel de DingTalk](https://open.dingtalk.com/document/development/download-the-server-side-sdk), sans appels HTTP écrits à la main. Les adaptateurs de fournisseur produisent des instantanés neutres des départements et des employés, si bien qu’un futur adaptateur Feishu pourra réutiliser la même logique de synchronisation, de liaison et de mise à jour des autorisations. diff --git a/docs/content/fr/guide/users.md b/docs/content/fr/guide/users.md index 50422253..5702370a 100644 --- a/docs/content/fr/guide/users.md +++ b/docs/content/fr/guide/users.md @@ -31,6 +31,7 @@ Recherchez par nom d’utilisateur, nom d’affichage ou adresse e-mail, filtrez | Réinitialiser l’authentification à deux facteurs | En cas de perte de l’authentificateur : désactive l’authentification à deux facteurs du compte et met fin à ses sessions | | Voir les rôles | Les rôles détenus par le compte et l’origine de chacun (affectation directe, groupe d’utilisateurs, service, poste) | | Voir les autorisations effectives | Voir [Explication, simulation et audit des autorisations](/fr/guide/explain/) | +| Autoriser directement | Choisir une application métier et autoriser à l’utilisateur seul des menus, pages, boutons ou API, avec une date d’expiration facultative | | Supprimer | Supprime le compte et ses rattachements aux services ; les enregistrements d’audit sont conservés | Le compte système (l’administrateur créé à l’initialisation) et votre propre compte ne peuvent être ni désactivés, ni verrouillés, ni supprimés. @@ -38,3 +39,5 @@ Le compte système (l’administrateur créé à l’initialisation) et votre pr ## Inscription libre Désactivée par défaut. Après avoir défini `grantforge.security.registration-enabled=true`, la page de connexion propose une entrée « Créer un compte » ; les comptes ainsi créés rejoignent le locataire désigné par `grantforge.security.registration-tenant` et sont des comptes ordinaires sans aucun rôle. + +Les autorisations directes se cumulent avec celles des rôles ; un refus venant d’un rôle reste prioritaire et les dépendances de ressources sont déduites comme d’habitude. La séparation des tâches fondée sur les rôles ne s’applique pas aux autorisations directes ; elles n’accordent ni politique de lignes ni politique de champs et ne peuvent pas servir pour l’application console. Seuls les administrateurs autorisés à attribuer des rôles et à modifier l’utilisateur concerné peuvent les gérer. diff --git a/docs/content/fr/integration/javascript.md b/docs/content/fr/integration/javascript.md index 19ba9db4..e7692f5a 100644 --- a/docs/content/fr/integration/javascript.md +++ b/docs/content/fr/integration/javascript.md @@ -48,7 +48,8 @@ app.use(createGrantForge(grantForge)) ```vue - ``` + +``` `useGrantForge()` fournit `authorization`, `error`, `refresh()`, `can()` et `hasResource()`. Masquer un bouton n’est qu’un confort : l’API de l’application doit vérifier les autorisations de la même manière. diff --git a/docs/content/fr/start/quick-start.md b/docs/content/fr/start/quick-start.md index ff473811..209d0cdf 100644 --- a/docs/content/fr/start/quick-start.md +++ b/docs/content/fr/start/quick-start.md @@ -38,7 +38,7 @@ Le journal de la version publiée se trouve dans `logs/grantforge.log` ; avec Do ## 2. Terminer l’initialisation -Ouvrez http://127.0.0.1:9999/ dans votre navigateur ; la console bascule automatiquement sur la page d’initialisation. Renseignez le jeton figurant dans le journal, le nom de l’organisation, ainsi que le nom d’utilisateur et le mot de passe du premier administrateur (au moins 12 caractères). +Ouvrez dans votre navigateur ; la console bascule automatiquement sur la page d’initialisation. Renseignez le jeton figurant dans le journal, le nom de l’organisation, ainsi que le nom d’utilisateur et le mot de passe du premier administrateur (au moins 12 caractères). ![Première initialisation et page de connexion](/screenshots/login.png) diff --git a/docs/content/it/changelog/2026.4.0.md b/docs/content/it/changelog/2026.4.0.md new file mode 100644 index 00000000..0f9d174a --- /dev/null +++ b/docs/content/it/changelog/2026.4.0.md @@ -0,0 +1,68 @@ +--- +title: "2026.4.0" +description: "GrantForge 2026.4.0 (2026-11-08): plug-in di servizio e agenti per Elasticsearch, Apache Doris, MongoDB e Redis/Valkey, sincronizzazione dell’organizzazione da DingTalk, autorizzazioni dirette degli utenti, esportazione e importazione del catalogo delle risorse e una CI che verifica che ogni lingua sia tradotta per intero." +--- + + +2026.4.0 estende i permessi dei sistemi esterni a Elasticsearch, Apache Doris, MongoDB, Redis e Valkey. Tutti e quattro gli agenti girano accanto al sistema e ne mantengono il controllo degli accessi allineato alle policy. Sul fronte di identità e autorizzazione arrivano la sincronizzazione dell’organizzazione da DingTalk, le autorizzazioni dirette degli utenti e l’esportazione e importazione del catalogo delle risorse. Tutte le modifiche sono nel [confronto su GitHub](https://github.com/devlive-community/grantforge/compare/v2026.3.0...v2026.4.0). + +## Elasticsearch + +- Plug-in di servizio Elasticsearch: indici e flussi di dati, i loro campi e il cluster; accesso con password, chiave API o certificato client TLS; vedi [Elasticsearch](/it/plugins/elasticsearch/). +- Agente accanto al cluster: mantiene per ogni utente un ruolo proprio di Elasticsearch, assegnato agli utenti nativi e, tramite mappature dei ruoli, agli utenti di altri realm, con sicurezza a livello di campo e di documento dove la licenza lo consente; i pattern di indice restano pattern, così i nuovi indici sono subito raggiungibili; vedi [Agente Elasticsearch](/it/external/elasticsearch-agent/). +- Gli accessi provengono dai file del log di audit. I test di integrazione coprono Elasticsearch 8.19, 9.3, 9.4 e 9.5. + +## Apache Doris + +- Plug-in di servizio Doris: cataloghi, database, tabelle, colonne e gruppi di carico di lavoro; si collega a uno o più FE e accede con password; vedi [Apache Doris](/it/plugins/doris/). +- Agente accanto al cluster: le policy diventano ruoli propri di Doris, permessi sulle colonne, permessi sui gruppi di carico di lavoro e policy di riga, concessi a ogni identità dell’utente; un intero database o catalogo viene concesso solo quando Doris non darebbe di più; vedi [Agente Apache Doris](/it/external/doris-agent/). +- Gli accessi provengono dalla tabella del log di audit di Doris. I test di integrazione coprono Doris 2.1, 3.1, 4.0 e 4.1. + +## MongoDB + +- Plug-in di servizio MongoDB: database e collezioni; si collega a un replica set o a un cluster con sharding e accede con SCRAM, certificato client, LDAP o Kerberos; vedi [MongoDB](/it/plugins/mongodb/). +- Agente accanto al deployment: mantiene un ruolo MongoDB per utente e ne sostituisce i privilegi in un solo passaggio; dai privilegi ereditati che MongoDB riporta, indica che cosa concedono davvero i ruoli integrati e personalizzati esterni a GrantForge; vedi [Agente MongoDB](/it/external/mongodb-agent/). +- Gli accessi provengono dai file del log di audit di Enterprise o Percona, oppure dal profiler. I test di integrazione coprono MongoDB 6.0, 7.0, 8.0 e 9.0, con Kerberos verificato su Percona Server for MongoDB. + +## Redis e Valkey + +- Plug-in di servizio Redis: chiavi, canali e nodi; si collega a un server singolo con le sue repliche o a un cluster e accede come utente ACL, facoltativamente via TLS; vedi [Redis](/it/plugins/redis/). +- Agente accanto al deployment: su ogni nodo allinea alle policy i selettori ACL di ogni utente e li sostituisce con un solo comando; i permessi radice restano agli operatori, e l’agente chiede a Redis con `ACL DRYRUN` se consentono accessi che le policy negano; vedi [Agente Redis](/it/external/redis-agent/). +- I rifiuti provengono dal log ACL di ogni nodo. I test di integrazione coprono Redis 7.2, 7.4, 8.2 e 8.10, Valkey 8.1 e 9.1 e un cluster Redis 8.10. + +## Agenti + +- Gli agenti di ClickHouse, PostgreSQL e OpenSearch e i nuovi agenti di questa versione che girano accanto a un sistema condividono il nuovo nucleo `grantforge-agent-sync`: avvio, impostazioni, timer, ciclo di sincronizzazione, posizioni di lettura dell’audit e lettura continua dei file di log. Ogni agente tiene solo ciò che è proprio del suo sistema; i formati di impostazioni e log non cambiano. + +## Identità e organizzazione + +- Origini dati: la sincronizzazione dell’organizzazione si configura separatamente dalle fonti di identità, e il primo adattatore è DingTalk, con il suo SDK server ufficiale; dopo un accesso SSO, gli utenti vengono collegati automaticamente ai propri reparti e al reparto principale tramite l’indirizzo e-mail attendibile; le tabelle migrano su tutti e sei i database supportati, SQL Server e Oracle compresi; vedi [Fonti di identità (LDAP e OIDC)](/it/guide/identity-sources/). +- Le fonti di identità OpenID Connect possono sincronizzare il numero di telefono e l’avatar e aggiungere parametri alla richiesta di autorizzazione (come `provider_hint` di Casdoor); i parametri propri del protocollo non si possono sovrascrivere. + +## Autorizzazione e catalogo delle risorse + +- Autorizzazioni dirette degli utenti: consentono a un solo utente menu, pagine, pulsanti o API di un’applicazione di business, facoltativamente fino a una scadenza; si sommano ai permessi dei ruoli, e un divieto da un ruolo resta prioritario; vedi [Utenti](/it/guide/users/). +- Il catalogo delle risorse può esportare l’albero delle risorse di un’applicazione in JSON e importarlo in un’altra applicazione: l’importazione viene prima verificata e, in caso di conflitto, non si applica nulla; gli amministratori della piattaforma possono svuotare gli alberi di applicazione idonei; vedi [Catalogo di risorse e API](/it/guide/catalog/). + +## Server + +- All’avvio il server mostra un banner GrantForge con la propria versione e quella di Java al posto del banner di Spring Boot. + +## Documentazione e pubblicazione + +- La scheda dei plug-in si apre con una panoramica, raggruppa i sistemi per tipo in una barra laterale comprimibile e indica per ciascun sistema come applica le policy, il supporto ai filtri di riga e al mascheramento e le versioni verificate; tutti i README elencano i componenti nello stesso ordine. +- La CI verifica che ogni pagina sia tradotta per intero in ogni lingua: gli stessi paragrafi, titoli, tabelle, blocchi di codice e link della pagina cinese, nessun testo nella scrittura di un’altra lingua e niente copiato dalla pagina inglese; gli 11 README passano gli stessi controlli rispetto al README inglese. Tutte le traduzioni mancanti trovate dal controllo sono state completate. +- Il controllo delle versioni mantiene aggiornato il badge di versione di ogni README. +- I workflow di pubblicazione della documentazione e delle versioni non girano più sui fork. +- Il server `grantforge-server` non viene più pubblicato nei repository Maven: include tutti i plug-in e supera il loro limite di dimensione; è distribuito nell’archivio della release e nell’immagine. + +## Correzioni + +- Cambiare il tipo di una risorsa nell’editor delle risorse cancella il codice di risorsa che non è più adatto. +- L’ultimo blocco di codice della guida all’integrazione JavaScript non inghiotte più il testo che lo segue, e l’indirizzo della console nelle pagine di Docker e dell’avvio rapido non trasforma più in link il testo successivo. +- Dipendenze aggiornate a versioni che correggono avvisi di sicurezza: MariaDB Connector/J 3.5.11, OpenTelemetry 1.67.0, Guava 33.7.2, Snappy 1.1.10.9, oltre a vue-i18n, glob e minimatch nella console e nell’SDK. diff --git a/docs/content/it/deploy/docker.md b/docs/content/it/deploy/docker.md index 1ca03bad..84320ce2 100644 --- a/docs/content/it/deploy/docker.md +++ b/docs/content/it/deploy/docker.md @@ -43,7 +43,7 @@ docker compose -f deploy/compose/postgres.yml up -d docker compose -f deploy/compose/postgres.yml logs grantforge | grep "setup token" ``` -Poi apri http://127.0.0.1:9999/. La password predefinita del database usata dagli esempi è adatta solo per le prove: cambiala con `GRANTFORGE_DB_PASSWORD` prima dell’uso in produzione. L’esempio MySQL richiede di aver prima inserito `mysql-connector-j-.jar` in `deploy/compose/drivers/`. +Poi apri . La password predefinita del database usata dagli esempi è adatta solo per le prove: cambiala con `GRANTFORGE_DB_PASSWORD` prima dell’uso in produzione. L’esempio MySQL richiede di aver prima inserito `mysql-connector-j-.jar` in `deploy/compose/drivers/`. ## Helm diff --git a/docs/content/it/develop/development.md b/docs/content/it/develop/development.md index 094b63f7..070efad5 100644 --- a/docs/content/it/develop/development.md +++ b/docs/content/it/develop/development.md @@ -76,7 +76,7 @@ Lo script richiede che l’area di lavoro sia pulita, che il ramo locale non sia - `script/ci/release.sh` compila il pacchetto di rilascio, una SBOM CycloneDX contenente solo le dipendenze di rilascio, e `SHA256SUMS`; - le immagini multi-architettura vengono inviate a `ghcr.io/devlive-community/grantforge`; -- gli artefatti Maven (con il pacchetto dei sorgenti e il Javadoc) vengono pubblicati su GitHub Packages; quando il repository ha configurato `CENTRAL_USERNAME`, `CENTRAL_PASSWORD` (token del Central Portal), `GPG_PRIVATE_KEY` e `GPG_PASSPHRASE`, dopo la firma vengono pubblicati su Maven Central; +- gli artefatti Maven (con il pacchetto dei sorgenti e il Javadoc) vengono pubblicati su GitHub Packages; quando il repository ha configurato `CENTRAL_USERNAME`, `CENTRAL_PASSWORD` (token del Central Portal), `GPG_PRIVATE_KEY` e `GPG_PASSPHRASE`, dopo la firma vengono pubblicati su Maven Central; il server `grantforge-server` include tutti i plug-in e supera il limite di dimensione dei repository, quindi non viene pubblicato in alcun repository Maven ed è distribuito solo nell’archivio della release e nell’immagine; - viene creata una GitHub Release il cui corpo raccoglie tutti i commit dall’ultima versione pubblicata (un tag `v*` o numerico come `1.0.6`), raggruppati per nuove funzionalità, correzioni, prestazioni, ecc., con i link ai commit. Le versioni candidate vengono contrassegnate come prerelease e non aggiornano il `latest` delle immagini. Con il profile `central` attivato in locale, per impostazione predefinita non si pubblica (`central.skip=true`); solo il flusso di lavoro di pubblicazione passa esplicitamente `-Dcentral.skip=false`. @@ -100,3 +100,5 @@ pnpm build # output in docs/out ``` Le pagine sono Markdown in `docs/content/` e la navigazione è in `docs/lib/navigation.ts`. Il riferimento dell’API e i codici di errore vengono generati in fase di compilazione dal contratto e dal codice sorgente. Le schermate vengono generate da `script/docs/screenshots.sh`, che avvia il servizio reale, scrive i dati di esempio e poi usa Playwright. + +`pnpm test` verifica anche le traduzioni: ogni pagina cinese deve essere tradotta in ciascuna delle altre dieci lingue, con gli stessi paragrafi, livelli di titolo, elenchi, tabelle, blocchi di codice e link della pagina cinese. Una pagina non può contenere testo in una scrittura che la sua lingua non usa (compresi caratteri semplificati nel cinese tradizionale e nel giapponese) né frasi copiate senza modifiche dalla pagina inglese. I 11 README nella radice del repository passano gli stessi controlli rispetto al README inglese, e i loro link a questo sito devono puntare a pagine esistenti della loro lingua. diff --git a/docs/content/it/guide/catalog.md b/docs/content/it/guide/catalog.md index c17f7d12..63f88a1e 100644 --- a/docs/content/it/guide/catalog.md +++ b/docs/content/it/guide/catalog.md @@ -22,6 +22,12 @@ description: Mantenere applicazioni e albero delle risorse, dipendenze delle ris - **Client OAuth**: registra client per le applicazioni di business; vedi [OAuth 2.1 e OpenID Connect](/it/integration/oauth/). - **Campi**: quando si seleziona un campo, mostra in quali interfacce compare (che lo restituiscono o lo ricevono). +Nel catalogo di un’applicazione con risorse, fai clic su **Esporta** per scaricare un file JSON di riferimento per lo sviluppo o per strumenti di IA. Contiene le informazioni dell’applicazione, tutte le risorse organizzate per gerarchia e le loro dipendenze, con i codici delle risorse alle due estremità; ricerca e filtri non cambiano il contenuto esportato. `formatVersion` indica la versione del formato di esportazione. + +Scegli l’applicazione di destinazione, fai clic su **Importa**, carica un file di esportazione della versione 1 ed esegui prima la verifica. L’importazione riusa le risorse con lo stesso codice nella stessa applicazione solo se tipo e padre coincidono, senza sovrascrivere le impostazioni esistenti; un conflitto di tipo, padre o tipo di dipendenza annulla l’intera importazione. Risorse e dipendenze nuove vengono scritte insieme dopo la conferma, e ripetere un’importazione non crea duplicati. Le applicazioni integrate non supportano l’importazione; le dipendenze dichiarate diventano manuali dopo l’importazione. Importare risorse richiede il permesso di creare risorse, e importare dipendenze richiede anche quello di modificarle. + +L’esportazione contiene solo ciò che è registrato in GrantForge: `route` di pagine e risorse simili è la route del frontend, `code` delle risorse API è il codice di permesso. Metodi HTTP e percorsi delle API di business esterne non vengono dedotti se non sono registrati. Il JSON è un’istantanea al momento dell’esportazione e non contiene le autorizzazioni di alcun utente; esporta di nuovo quando le risorse cambiano. + ## Catalogo API **Gestione della piattaforma → Catalogo API** elenca tutte le interfacce che il server registra automaticamente all’avvio e i requisiti di accesso di ciascuna: pubbliche, sufficiente aver effettuato l’accesso, oppure necessario un codice di permesso. Le interfacce che richiedono autorizzazione vengono raggruppate nel catalogo di risorse in base al codice di permesso e i ruoli citano questi permessi quando vengono autorizzati. Quando un’interfaccia viene aggiunta, rimossa o cambia il suo codice di permesso, compare come “modifica in attesa di conferma” e il catalogo si aggiorna una volta confermata. diff --git a/docs/content/it/guide/explain.md b/docs/content/it/guide/explain.md index c1becfed..ee03f88a 100644 --- a/docs/content/it/guide/explain.md +++ b/docs/content/it/guide/explain.md @@ -13,6 +13,8 @@ description: Consultare che cosa può fare una persona e perché, simulare le mo In **Gestione utenti**, fai clic su “Visualizza permessi in vigore” sulla riga di un utente per vedere tutto ciò che può fare in questo momento: ruoli, menu e pulsanti, API, ambito sui dati e campi limitati. +Scegli un’applicazione per vederne le risorse e i permessi API in vigore; “Perché” mostra se derivano da ruoli o da autorizzazioni dirette dell’utente. Ruoli, ambiti dei dati e permessi sui campi mostrano la configurazione dell’intero account. Simulando l’aggiunta o la rimozione di ruoli, le autorizzazioni dirette esistenti restano valide. + ![Permessi in vigore](/screenshots/user-permissions.png) Fai clic su **Spiegazione** su una qualsiasi di queste voci e GrantForge risponderà “perché può farlo”: da quale ruolo, attraverso quali assegnazioni (diretta, gruppo di utenti, dipartimento, posizione) e da quali risorse viene derivato; quando non può farlo, indica se manca l’autorizzazione oppure se a bloccarlo è una qualche negazione. diff --git a/docs/content/it/guide/identity-sources.md b/docs/content/it/guide/identity-sources.md index 48f82192..c2287ec9 100644 --- a/docs/content/it/guide/identity-sources.md +++ b/docs/content/it/guide/identity-sources.md @@ -44,6 +44,11 @@ Scegli il tipo “OpenID Connect” per collegare, per esempio, Keycloak, Azure | ID / segreto del client | Il client registrato presso il fornitore; in assenza di segreto viene usato come client pubblico con PKCE | | Indirizzo di callback | L’indirizzo `/api/v1/auth/federated/callback/` mostrato nella pagina deve essere registrato nel client del fornitore | | Scope e claim | Per impostazione predefinita `openid profile email`; nome utente, nome visualizzato e indirizzo e-mail vengono letti rispettivamente dalle claim `preferred_username`, `name` ed `email` | +| Claim del telefono | Legge `phone` per impostazione predefinita (Casdoor); per altri provider si può indicare `phone_number`. Per sincronizzare il numero di telefono lo scope deve includere `phone`. Se all’accesso il provider non restituisce un numero, resta quello esistente | +| Indirizzo dell’avatar | Sincronizzato dalla claim OIDC standard `picture`; lo scope deve includere `profile`. Se non viene restituito, resta l’indirizzo esistente | +| Parametri aggiuntivi della richiesta di autorizzazione | Oggetto JSON facoltativo i cui valori devono essere stringhe. Con Casdoor, ad esempio, `{"provider_hint":"github"}` sceglie un provider configurato. I parametri compaiono nell’indirizzo del browser, quindi non inserirvi mai segreti | + +GrantForge gestisce i parametri essenziali del protocollo (compresi `state`, `nonce`, `redirect_uri` e quelli di PKCE); i parametri aggiuntivi non possono sovrascriverli. Una volta abilitata, nella pagina di accesso compare il pulsante “Accedi con ”. L’utente accede presso il fornitore e torna in GrantForge; gli account con l’autenticazione a due fattori attivata devono inserire anche un codice di verifica. @@ -54,3 +59,26 @@ Una volta abilitata, nella pagina di accesso compare il pulsante “Accedi con < - Una fonte di identità che non crea account in automatico consente l’accesso solo agli utenti già associati. - Dopo la disattivazione di una fonte di identità, i suoi utenti non possono più accedere; una fonte di identità ancora utilizzata da degli account non può essere eliminata. - Agli account di una fonte di identità è possibile assegnare ruoli e attivare l’autenticazione a due fattori come di consueto. + +La sincronizzazione della struttura organizzativa si configura in un’origine dati separata, senza modificare le impostazioni di accesso OIDC. + +## Origini dati e sincronizzazione dell’organizzazione + +Le origini dati sincronizzano la struttura organizzativa, mentre le fonti di identità continuano a gestire l’accesso OIDC o LDAP. Le due si configurano separatamente: disattivare un’origine dati non influisce sull’accesso OIDC. + +1. Configura l’accesso OpenID Connect esistente in “Controllo accessi → Fonti di identità”. +2. In “Controllo accessi → Origini dati” aggiungi un’origine DingTalk e scegli la fonte di identità OIDC collegata. +3. Inserisci CorpId, AppKey, AppSecret e l’ID del reparto radice (di solito `1`). L’AppSecret è cifrato a parte, l’API non lo restituisce mai in chiaro e, se in modifica il campo resta vuoto, si mantiene il valore precedente. +4. L’origine può essere montata sotto un reparto locale. L’intervallo di sincronizzazione predefinito è di 30 minuti e ammette da 15 a 10080 minuti; un campo vuoto significa solo sincronizzazione manuale. +5. Dopo il salvataggio, verifica la connessione ed esegui la prima sincronizzazione. L’applicazione DingTalk deve poter leggere i reparti e la rubrica dei dipendenti, e il suo ambito autorizzato deve coprire il sottoalbero scelto. +6. Quando un utente accede tramite SSO, il dipendente viene trovato tramite l’indirizzo e-mail OIDC attendibile e i suoi reparti, compreso quello principale, vengono compilati automaticamente. L’indirizzo deve essere gestito da un amministratore, non deve consentire a un dipendente di spacciarsi per un altro e deve coincidere con quello di DingTalk. Se manca l’indirizzo del dipendente, si usa quello aziendale. + +L’accesso legge solo l’istantanea locale della rubrica e non interroga DingTalk. Con l’MFA, l’appartenenza viene aggiornata dopo il superamento del secondo passaggio. Gli account che accedono prima della prima sincronizzazione conservano l’indirizzo attendibile e vengono collegati al termine della sincronizzazione; l’indirizzo del profilo, modificabile localmente, non viene mai usato per l’abbinamento. Indirizzi mancanti, duplicati o già collegati a un altro account non creano nuovi collegamenti. + +Il collegamento a un dipendente si basa sul suo ID esterno e non passa a un altro dipendente quando cambia l’indirizzo. La sincronizzazione gestisce l’appartenenza a più reparti; il reparto principale resta quello precedente finché è valido, altrimenti viene scelto secondo l’ordine stabile degli ID di reparto. Reparti locali, gruppi di utenti, posizioni e assegnazioni dirette di ruoli vengono mantenuti. Assegnando un ruolo a un reparto sincronizzato, gli utenti lo ereditano secondo le regole esistenti, e un trasferimento aggiorna i permessi. + +Non appena esistono un’istantanea dell’organizzazione o account in attesa di collegamento, la fonte di identità collegata, l’azienda, l’applicazione, il reparto radice e il punto di montaggio restano fissi. Reparti e appartenenze sincronizzati sono in sola lettura e non possono essere sovrascritti a mano dalla gestione degli utenti o dell’organizzazione. I reparti scomparsi vengono mantenuti per proteggere le autorizzazioni esistenti; quando un dipendente esce dalla rubrica autorizzata, le appartenenze gestite da questa origine vengono rimosse, ma l’account non viene disattivato automaticamente. Un’origine dati che possiede reparti non può essere eliminata direttamente, ma può essere disattivata. + +La sincronizzazione recupera tutte le pagine prima di applicare il risultato in modo atomico. In caso di errori di lettura, gerarchie anomale o modifiche dell’ambito autorizzato si mantiene l’ultima istantanea riuscita; gli amministratori verificano il risultato tramite il riepilogo della sincronizzazione, l’elenco delle anomalie e il registro di audit. Se l’ambito è cambiato, ripristina prima l’ambito approvato e poi sincronizza di nuovo. + +L’accesso usa l’[SDK server ufficiale di DingTalk](https://open.dingtalk.com/document/development/download-the-server-side-sdk), senza chiamate HTTP scritte a mano. Gli adattatori dei fornitori producono istantanee neutre di reparti e dipendenti, così un futuro adattatore Feishu potrà riutilizzare la stessa logica di sincronizzazione, collegamento e aggiornamento dei permessi. diff --git a/docs/content/it/guide/users.md b/docs/content/it/guide/users.md index 152e35a6..bcbb0e00 100644 --- a/docs/content/it/guide/users.md +++ b/docs/content/it/guide/users.md @@ -31,6 +31,7 @@ Cerca per nome utente, nome visualizzato o indirizzo e-mail, filtra per stato (a | Reimposta autenticazione a due fattori | Per la perdita dell’autenticatore: disattiva l’autenticazione a due fattori dell’account e ne chiude le sessioni | | Visualizza ruoli | I ruoli posseduti dall’account e l’origine di ciascuno (assegnazione diretta, gruppo di utenti, dipartimento, posizione) | | Visualizza permessi in vigore | Vedi [Spiegazione, simulazione e audit](/it/guide/explain/) | +| Autorizzare direttamente | Scegliere un’applicazione di business e consentire solo a questo utente menu, pagine, pulsanti o API, con scadenza facoltativa | | Elimina | Elimina l’account e le sue relazioni con i dipartimenti; i record di audit vengono conservati | L’account di sistema (l’amministratore creato in fase di inizializzazione) e il proprio account non possono essere disattivati, bloccati né eliminati. @@ -38,3 +39,5 @@ L’account di sistema (l’amministratore creato in fase di inizializzazione) e ## Registrazione autonoma Disattivata per impostazione predefinita. Dopo aver impostato `grantforge.security.registration-enabled=true`, nella pagina di accesso compare la voce “Crea account”; gli account registrati entrano nel tenant indicato da `grantforge.security.registration-tenant` e sono account ordinari privi di qualsiasi ruolo. + +Le autorizzazioni dirette si sommano a quelle dei ruoli; un divieto da un ruolo resta prioritario e le dipendenze tra risorse si deducono come di consueto. La separazione dei compiti basata sui ruoli non si applica alle autorizzazioni dirette; non concedono criteri su righe o campi e non si possono usare per l’applicazione della console. Possono gestirle solo gli amministratori autorizzati ad assegnare ruoli e a modificare l’utente di destinazione. diff --git a/docs/content/it/integration/javascript.md b/docs/content/it/integration/javascript.md index 39c1d162..b928252c 100644 --- a/docs/content/it/integration/javascript.md +++ b/docs/content/it/integration/javascript.md @@ -48,7 +48,8 @@ app.use(createGrantForge(grantForge)) ```vue - ``` + +``` `useGrantForge()` fornisce `authorization`, `error`, `refresh()`, `can()` e `hasResource()`. Nascondere i pulsanti è solo una comodità: l’API dell’applicazione deve verificare i permessi allo stesso modo. diff --git a/docs/content/it/start/quick-start.md b/docs/content/it/start/quick-start.md index b30dfb65..81c76651 100644 --- a/docs/content/it/start/quick-start.md +++ b/docs/content/it/start/quick-start.md @@ -38,7 +38,7 @@ I log del pacchetto di rilascio si trovano in `logs/grantforge.log`; con Docker ## 2. Completare l’inizializzazione -Apri http://127.0.0.1:9999/ nel browser: la console entra direttamente nella pagina di inizializzazione. Inserisci il token riportato nel log, il nome dell’organizzazione e il nome utente e la password del primo amministratore (almeno 12 caratteri). +Apri nel browser: la console entra direttamente nella pagina di inizializzazione. Inserisci il token riportato nel log, il nome dell’organizzazione e il nome utente e la password del primo amministratore (almeno 12 caratteri). ![Pagina di inizializzazione e di accesso](/screenshots/login.png) diff --git a/docs/content/ja/changelog/2026.4.0.md b/docs/content/ja/changelog/2026.4.0.md new file mode 100644 index 00000000..06ae556a --- /dev/null +++ b/docs/content/ja/changelog/2026.4.0.md @@ -0,0 +1,68 @@ +--- +title: "2026.4.0" +description: "GrantForge 2026.4.0(2026-11-08):Elasticsearch、Apache Doris、MongoDB、Redis/Valkey のサービスプラグインとエージェント、DingTalk の組織構造の同期、ユーザーへの直接付与、リソースカタログのエクスポートとインポート、各言語の翻訳が揃っていることを確認する CI。" +--- + + +2026.4.0 は外部システム権限を Elasticsearch、Apache Doris、MongoDB、Redis と Valkey に拡張します。4 つのエージェントはいずれもシステムの横で動き、システム自身のアクセス制御をポリシーに合わせ続けます。アイデンティティと認可では、DingTalk の組織構造の同期、ユーザーへの直接付与、リソースカタログのエクスポートとインポートが加わりました。すべての変更は [GitHub の比較](https://github.com/devlive-community/grantforge/compare/v2026.3.0...v2026.4.0) を参照してください。 + +## Elasticsearch + +- Elasticsearch サービスプラグイン:インデックスとデータストリーム、そのフィールド、クラスター。パスワード、API キー、TLS クライアント証明書でログインします。[Elasticsearch](/ja/plugins/elasticsearch/) を参照してください。 +- クラスターの横で動くエージェント:ユーザーごとに Elasticsearch 自身のロールを維持し、ネイティブユーザーに割り当て、ほかの realm のユーザーにはロールマッピングで割り当てます。ライセンスが許せばフィールドレベルとドキュメントレベルのセキュリティも付けます。インデックスパターンはパターンのまま保たれ、新しいインデックスにもすぐに届きます。[Elasticsearch エージェント](/ja/external/elasticsearch-agent/) を参照してください。 +- アクセスの記録は監査ログファイルから取得します。統合テストは Elasticsearch 8.19、9.3、9.4、9.5 をカバーします。 + +## Apache Doris + +- Doris サービスプラグイン:カタログ、データベース、テーブル、列、ワークロードグループ。1 台以上の FE に接続し、パスワードでログインします。[Apache Doris](/ja/plugins/doris/) を参照してください。 +- クラスターの横で動くエージェント:ポリシーは Doris 自身のロール、列の権限付与、ワークロードグループの権限付与、行ポリシーになり、ユーザーのすべてのアイデンティティに付与されます。データベースやカタログ全体への付与は、それによって Doris がより多くを与えない場合に限ります。[Apache Doris エージェント](/ja/external/doris-agent/) を参照してください。 +- アクセスの記録は Doris の監査ログテーブルから取得します。統合テストは Doris 2.1、3.1、4.0、4.1 をカバーします。 + +## MongoDB + +- MongoDB サービスプラグイン:データベースとコレクション。レプリカセットまたはシャードクラスターに接続し、SCRAM、クライアント証明書、LDAP、Kerberos でログインします。[MongoDB](/ja/plugins/mongodb/) を参照してください。 +- デプロイメントの横で動くエージェント:ユーザーごとに MongoDB のロールを 1 つ維持し、その権限を一度に置き換えます。MongoDB が返す継承された権限から、GrantForge の外にある組み込みロールやカスタムロールが実際に何を与えているかを報告します。[MongoDB エージェント](/ja/external/mongodb-agent/) を参照してください。 +- アクセスの記録は Enterprise または Percona の監査ログファイル、あるいはプロファイラーから取得します。統合テストは MongoDB 6.0、7.0、8.0、9.0 をカバーし、Kerberos は Percona Server for MongoDB で検証しています。 + +## Redis と Valkey + +- Redis サービスプラグイン:キー、チャンネル、ノード。単体サーバーとそのレプリカ、またはクラスターに接続し、ACL ユーザーとしてログインします。TLS も使えます。[Redis](/ja/plugins/redis/) を参照してください。 +- デプロイメントの横で動くエージェント:すべてのノードで各ユーザーの ACL セレクターをポリシーに合わせ、1 つのコマンドでまとめて置き換えます。ルートの権限は引き続き運用担当者が管理し、エージェントはそれがポリシーの拒否するアクセスを許していないかを `ACL DRYRUN` で Redis に確かめます。[Redis エージェント](/ja/external/redis-agent/) を参照してください。 +- 拒否の記録は各ノードの ACL ログから取得します。統合テストは Redis 7.2、7.4、8.2、8.10 と Valkey 8.1、9.1、さらに Redis 8.10 のクラスターをカバーします。 + +## エージェント + +- ClickHouse、PostgreSQL、OpenSearch のエージェントと、このリリースで加わったシステムの横で動くエージェントは、新しい `grantforge-agent-sync` コアを共有します。起動処理、設定、タイマー、同期の流れ、監査の読み取り位置、ログファイルの追跡がそこにあります。各エージェントはそのシステム固有の部分だけを持ち、設定とログの形式は変わりません。 + +## アイデンティティと組織 + +- データソース:組織構造の同期はアイデンティティソースとは別に設定し、最初のアダプターは公式のサーバー SDK を使う DingTalk です。SSO でログインしたユーザーは、信頼できるメールアドレスによって部署と主所属部署に自動で関連付けられます。そのテーブルはサポートする 6 種類すべてのデータベースで、SQL Server と Oracle も含めて移行されます。[アイデンティティソース(LDAP と OIDC)](/ja/guide/identity-sources/) を参照してください。 +- OpenID Connect のアイデンティティソースは電話番号とアバターを同期でき、認可リクエストにパラメーター(Casdoor の `provider_hint` など)を追加できます。プロトコル自身のパラメーターは上書きできません。 + +## 認可とリソースカタログ + +- ユーザーへの直接付与:業務アプリケーションのメニュー、ページ、ボタン、API を 1 人のユーザーだけに許可し、有効期限も設定できます。ロールの権限と合わせて計算され、ロールによる拒否が引き続き優先します。[ユーザー](/ja/guide/users/) を参照してください。 +- リソースカタログはアプリケーションのリソースツリーを JSON としてエクスポートし、別のアプリケーションにインポートできます。インポートはまず事前チェックされ、衝突があれば何も適用されません。プラットフォーム管理者は条件を満たすアプリケーションのツリーを空にできます。[リソースカタログと API カタログ](/ja/guide/catalog/) を参照してください。 + +## サーバー + +- 起動時には Spring Boot のバナーではなく、GrantForge のバナー、バージョン、Java のバージョンが表示されます。 + +## ドキュメントとリリース + +- プラグインのタブは概要から始まり、システムを種類ごとに折りたためるサイドバーにまとめ、各システムの適用方式、行フィルターとマスキングへの対応、検証済みのバージョンを一覧にします。各 README も同じ順序でコンポーネントを並べます。 +- CI は、すべてのページがすべての言語に完全に翻訳されていることを確認します。段落、見出し、表、コードブロック、リンクが中国語のページと一致し、ほかの言語の文字を含まず、英語のページから写した文もないことです。11 個の README も英語の README を基準に同じ確認を受けます。この確認で見つかった翻訳の不足はすべて補いました。 +- バージョンの確認がすべての README のバージョンバッジを最新に保ちます。 +- ドキュメントの公開とリリースのワークフローはフォークでは実行されなくなりました。 +- サーバー `grantforge-server` は Maven リポジトリーに公開しなくなりました。すべてのプラグインを同梱しておりリポジトリーのサイズ上限を超えるためで、サーバーはリリースアーカイブとイメージで提供されます。 + +## 修正 + +- リソースの編集画面でリソースの種類を変えると、合わなくなったリソースコードが消去されます。 +- JavaScript 連携ガイドの最後のコードブロックが後ろの本文を飲み込まなくなり、Docker とクイックスタートのページにあるコンソールのアドレスが後ろの文字をリンクにしなくなりました。 +- 依存関係をセキュリティアドバイザリーを修正したバージョンに更新しました:MariaDB Connector/J 3.5.11、OpenTelemetry 1.67.0、Guava 33.7.2、Snappy 1.1.10.9、およびコンソールと SDK の vue-i18n、glob、minimatch。 diff --git a/docs/content/ja/deploy/docker.md b/docs/content/ja/deploy/docker.md index 505e40ed..c6cbb47c 100644 --- a/docs/content/ja/deploy/docker.md +++ b/docs/content/ja/deploy/docker.md @@ -43,7 +43,7 @@ docker compose -f deploy/compose/postgres.yml up -d docker compose -f deploy/compose/postgres.yml logs grantforge | grep "setup token" ``` -その後 http://127.0.0.1:9999/ を開きます。例で使っている既定のデータベースパスワードは試用にしか適さないため、実際に使う前に `GRANTFORGE_DB_PASSWORD` で変更してください。MySQL の例では、まず `mysql-connector-j-<バージョン>.jar` を `deploy/compose/drivers/` に入れる必要があります。 +その後 を開きます。例で使っている既定のデータベースパスワードは試用にしか適さないため、実際に使う前に `GRANTFORGE_DB_PASSWORD` で変更してください。MySQL の例では、まず `mysql-connector-j-<バージョン>.jar` を `deploy/compose/drivers/` に入れる必要があります。 ## Helm diff --git a/docs/content/ja/develop/development.md b/docs/content/ja/develop/development.md index 23353abf..a8dcd751 100644 --- a/docs/content/ja/develop/development.md +++ b/docs/content/ja/develop/development.md @@ -76,7 +76,7 @@ bash script/release/tag.sh 2026.1.0 --next 2026.2.0 # バージョンを設 - `script/ci/release.sh` がリリースパッケージ、リリース依存のみを含む CycloneDX SBOM、`SHA256SUMS` をビルドします。 - マルチアーキテクチャのイメージを `ghcr.io/devlive-community/grantforge` にプッシュします。 -- Maven アーティファクト(ソースパッケージと Javadoc を含む)を GitHub Packages に公開します。リポジトリに `CENTRAL_USERNAME`、`CENTRAL_PASSWORD`(Central Portal のトークン)、`GPG_PRIVATE_KEY`、`GPG_PASSPHRASE` が設定されている場合は、署名して Maven Central に公開します。 +- Maven アーティファクト(ソースパッケージと Javadoc を含む)を GitHub Packages に公開します。リポジトリに `CENTRAL_USERNAME`、`CENTRAL_PASSWORD`(Central Portal のトークン)、`GPG_PRIVATE_KEY`、`GPG_PASSPHRASE` が設定されている場合は、署名して Maven Central に公開します。サーバー `grantforge-server` はすべてのプラグインを同梱しておりリポジトリーのサイズ上限を超えるため、どの Maven リポジトリーにも公開せず、リリースアーカイブとイメージでのみ提供します。 - GitHub Release を作成します。本文には前回のリリース(`v*` や `1.0.6` のような数値タグ)以降のすべてのコミットを、新機能、バグ修正、性能などにグループ化してコミットリンクとともに掲載します。 リリース候補はプレリリースとしてマークされ、イメージの `latest` は更新しません。ローカルで `central` プロファイルを有効にしてもデフォルトでは公開しません(`central.skip=true`)。リリースワークフローだけが `-Dcentral.skip=false` を明示的に渡します。 @@ -100,3 +100,5 @@ pnpm build # docs/out に出力 ``` ページは `docs/content/` 配下の Markdown で、ナビゲーションは `docs/lib/navigation.ts` にあります。API リファレンスとエラーコードはビルド時にコントラクトとソースコードから生成します。スクリーンショットは `script/docs/screenshots.sh` が実際のサービスを起動してサンプルデータを書き込んだあと、Playwright で生成します。 + +`pnpm test` は翻訳も確認します。すべての中国語ページには残り 10 言語それぞれの翻訳が必要で、段落、見出しのレベル、リスト、表、コードブロック、リンクが中国語ページと一致していなければなりません。ページには、その言語が使わない文字(繁体字中国語や日本語に紛れた簡体字を含む)も、英語ページから写しただけの文も含めてはいけません。リポジトリー直下の 11 個の README も英語の README を基準に同じ確認を受け、このサイトへのリンクはその言語の実在するページを指す必要があります。 diff --git a/docs/content/ja/external/hdfs-agent.md b/docs/content/ja/external/hdfs-agent.md index a97e83a9..7756a3a0 100644 --- a/docs/content/ja/external/hdfs-agent.md +++ b/docs/content/ja/external/hdfs-agent.md @@ -9,71 +9,69 @@ description: Hadoop バージョンに対応する NameNode エージェント project root for full license text. --> -サーバープラグイン `grantforge-plugin-hdfs` がリソースと接続を定義します。Hadoop 2.7、2.10、3.2、3.3、3.4、3.5 向けの番号付き NameNode エージェントが、それぞれの SPI でアクセスを検査し、署名付きスナップショット、ポリシー評価、監査報告のロジックを共有します。 +サーバー側の `grantforge-plugin-hdfs` が `hdfs` サービス種別、リソース、接続設定をまとめて提供します。NameNode エージェントは Hadoop のバージョンごとにビルドされ、そのバージョンの `INodeAttributeProvider` と `AccessControlEnforcer` でアクセスを検査し、署名付きポリシー、キャッシュ、ポリシー評価、監査のロジックを共有します。 -共有 HDFS ロジックは `agents/grantforge-agent-hdfs-common`、バージョン別アダプターは `agents/grantforge-agent-hdfs-` にあります。共有のネイティブアダプターは、Java 8 / Hadoop 2.7.7 を基準とする本番 Maven モジュール `agents/grantforge-agent-hdfs-native` に置きます。各番号付きモジュールはそのバイナリを Maven 依存関係として使用し、バージョン固有の入口とコールバックを保持します。共有の本番ソースを各モジュールで再コンパイルしません。`core/grantforge-agent-core` は共通のプロトコルと実行基盤として残ります。 +ソースでは、サーバー側プラグインは `plugins/grantforge-plugin-hdfs`、バージョン別エージェントは `agents/grantforge-agent-hdfs-`、Hadoop に依存しない共通の本番ロジックは `agents/grantforge-agent-hdfs-common` にあります。共通のネイティブアダプターは正式な Maven 本番モジュール `agents/grantforge-agent-hdfs-native` にあり、Java 8 / Hadoop 2.7.7 を基準にしています。番号付きの各モジュールは Maven のバイナリ依存としてこれを参照し、バージョンごとの入口と固有のコールバックだけを持ち、共通の本番ソースを重複してコンパイルしません。プロトコル、スナップショットキャッシュ、監査の基盤は引き続き `core/grantforge-agent-core` にあります。 -サーバープラグインは単一の `hdfs` サービスタイプを維持し、クライアントのバージョンはエージェントから独立しています。Hadoop 2.x の接続とパス検索には `webhdfs://namenode:50070`、HTTPS では対応する `swebhdfs://` を使用します。Hadoop 3.x は RPC `hdfs://` または WebHDFS を使用できます。コンテナテストは 2.x の WebHDFS と 3.x の両プロトコルを検証します。2.x の RPC は未認定で、設定したプロトコルを自動変更しません。 +サーバー側プラグインは `hdfs` 種別を 1 つだけ持ち、クライアントのバージョンはエージェントのバージョンとは独立しています。Hadoop 2.x の接続とパス検索には `webhdfs://namenode:50070`(HTTPS を有効にした場合は対応する `swebhdfs://` アドレス)を使います。Hadoop 3.x では RPC の `hdfs://` と WebHDFS のどちらも使えます。コンテナーのマトリクスは 2.x の WebHDFS と 3.x の RPC・WebHDFS を検証しています。2.x の RPC は認定しておらず、接続プロトコルを自動で切り替えることはありません。 -inode 属性拡張を有効にした Apache Hadoop 2.7.7 では、一般ユーザーが `/` を直接照会すると、エージェントのコールバック前にネイティブコードで null ポインターエラーが発生します。このバージョンのデータ操作と `lookup.path` には `/data` などの実際のデータディレクトリを使用してください。コンテナテストはこのルートパスの制限も明示的に検証します。 +Apache Hadoop 2.7.7 では inode 属性の拡張を有効にすると、一般ユーザーがルートパス `/` を直接問い合わせたときに、エージェントのコールバックより前にネイティブコードでヌルポインターが発生します。このバージョンではデータ操作とサービスの `lookup.path` に `/data` のような実際のデータディレクトリーを使ってください。テストはルートパスが失敗することを独立したアサーションとして残しています。 -## HDFS エージェントの対象バージョン +## バージョンの選択 -| Hadoop 基準バージョン | コンテナーの Java | エージェントのディレクトリー | -| --- | --- | --- | -| 2.7.7 | Java 8 | `agents/hdfs/2.7/` | -| 2.10.2 | Java 8 | `agents/hdfs/2.10/` | -| 3.2.4 | Java 8 | `agents/hdfs/3.2/` | -| 3.3.6 | Java 8(amd64 イメージ) | `agents/hdfs/3.3/` | -| 3.4.3 | Java 11 | `agents/hdfs/3.4/` | -| 3.5.0 | Java 17 | `agents/hdfs/3.5/` | +| エージェントモジュールのサフィックス | 検証済みの Apache Hadoop | コンテナーの JVM | 認可インターフェース | スーパーユーザーパスのコールバック | +| --- | --- | --- | --- | --- | +| `2.7` | 2.7.7 | Java 8 | パラメーター方式 | なし | +| `2.10` | 2.10.2 | Java 8 | パラメーター方式 | なし | +| `3.2` | 3.2.4 | Java 8 | パラメーター方式 | なし | +| `3.3` | 3.3.6 | Java 8 | コンテキスト方式 | なし | +| `3.4` | 3.4.3 | Java 11 | コンテキスト方式、スーパーユーザーと拒否のコールバック | あり | +| `3.5` | 3.5.0 | Java 17 | コンテキスト方式、スーパーユーザーと拒否のコールバック | あり | -クラスターの Hadoop 系列に合わせて、`agents/hdfs//` から `grantforge-agent-hdfs--.jar` を選択します。共有ロジックは Java 8、3.5 アダプターは Java 17 を対象とします。 +たとえば Hadoop 2.10.2 には `agents/hdfs/2.10/grantforge-agent-hdfs-2.10-.jar` をインストールします。各 NameNode にはバージョン別のエージェントを 1 つだけインストールし、アップグレード時は古い jar を削除します。設定の provider クラス名は変わりません。エージェントは起動時に Hadoop のメジャー/マイナーバージョンと必須のコールバックを確認し、バージョンが違えば起動を中止します。同じメジャー/マイナーバージョンのほかのパッチやベンダーのディストリビューションは個別に検証する必要があります。 -Hadoop 2.7、2.10、3.2、3.3 には、エージェントが利用するスーパーユーザー認可コールバックがありません。これらのスーパーユーザーのアクセスは Hadoop が管理します。GrantForge ポリシーで制御するデータアクセスには一般ユーザーを使ってください。 +`common` と `2.7`〜`3.4` のエージェント本体は Java 8 のバイトコード、`3.5` は Java 17 を使います。上の表は統合テストで実際に使った JVM であり、3.4.3 を Java 8 で検証したという意味ではありません。3.3.6 の公式テストイメージは amd64 だけを提供しているため、ARM ホストでは明示的に amd64 のエミュレーションで実行します。 ## 権限の関係 -以下の適用動作は一般ユーザーのアクセスを説明します。スーパーユーザーの制限は上記およびコールバックの説明を参照してください。 - -エージェントはまず HDFS のネイティブ権限チェックを行い、その後に GrantForge のポリシーを適用します。ユーザーはネイティブ権限とポリシーの両方を満たす必要があります。GrantForge の許可ポリシーは POSIX 権限、ACL、所有者チェック、sticky bit を迂回することはなく、拒否ポリシーは常に拒否します。ネイティブ権限の設定は、引き続き Hadoop の管理ツールで保守します。 +エージェントは先に HDFS ネイティブの権限チェックを行い、その後で GrantForge のポリシーを適用します。ユーザーはネイティブの権限とポリシーの両方を満たす必要があります。GrantForge の許可ポリシーが POSIX 権限、ACL、所有者チェック、sticky bit を迂回することはなく、拒否ポリシーは常に拒否します。ネイティブの権限設定は引き続き Hadoop の管理ツールで維持します。 -既定値の `grantforge.hdfs.native.fallback=false` では、ローカルのポリシースナップショットがない場合、一致するポリシーがない場合、またはエージェントがまだ起動していない場合に、データアクセスを拒否します。`true` に設定すると、ポリシーが判定しなかったアクセスはネイティブ権限を使い、明示的な拒否ポリシーはそのまま有効です。サーバーに一時的に到達できない間も、最後に署名検証を通ったローカルのスナップショットを使い続けます。 +既定値は `grantforge.hdfs.native.fallback=false` です。ローカルのポリシースナップショットがない、一致するポリシーがない、またはエージェントがまだ起動していない場合、データアクセスは拒否されます。`true` にすると、ポリシーで決まらないアクセスにはネイティブの権限を使います。明示的な拒否ポリシーは引き続き有効です。サーバーに一時的に到達できない間は、署名検証に合格した最後のローカルスナップショットを使い続けます。 -1 回の認可コールバックに含まれる祖先、対象、サブツリー、スナップショットのパス投影は、すべて同じバージョンのポリシースナップショットを使います。更新されたポリシーは次のコールバックから有効になるため、異なるバージョンの許可ルールが組み合わさることはありません。アクセス監査には、実際に使われたポリシーのバージョンが記録されます。 +1 回の認可コールバック内の祖先、対象、サブツリー、スナップショットパスの投影には同じバージョンのポリシースナップショットを使います。更新されたポリシーは次のコールバックから有効になり、異なるバージョンの許可ルールが混ざることはありません。アクセス監査には実際に使ったポリシーのバージョンを記録します。 -エージェントは一般ユーザーが対象にアクセスするために必要な `read`、`write`、`execute` を検査し、親ディレクトリー、祖先ディレクトリー、再帰的な検証が必要なサブディレクトリーも検査します。作成、削除、名前変更などの操作は複数のパスに関わるため、許可ポリシーがそれらをすべてカバーしている必要があります。厳格モードでは、対象ファイルに `read` ポリシーがあるだけでは不十分で、ユーザーに祖先ディレクトリーの `execute` ポリシーが必要です。たとえば `/` の `execute` を許可して再帰をチェックし、その後、実際のデータディレクトリーに読み取りと書き込みの権限を付与してください。 +エージェントは一般ユーザーが対象にアクセスするのに必要な `read`、`write`、`execute` を検査し、親ディレクトリー、祖先ディレクトリー、再帰的な確認が必要なサブディレクトリーも検査します。作成、削除、名前変更などの操作は複数のパスに関わるため、許可ポリシーはそれらをすべてカバーしなければなりません。厳格モードでは対象ファイルの `read` ポリシーだけでは足りず、祖先ディレクトリーの `execute` ポリシーもユーザーに設定する必要があります。たとえば `/` に対する `execute` を再帰付きで許可し、実際のデータディレクトリーに読み書きの権限を設定します。 -スナップショットのパスは、実際に要求されたパスと、`.snapshot/<スナップショット名>` を除いた元のパスの両方を検査します。たとえば `/data/.snapshot/s1/secret` は `/data/secret` としても検査されます。これにより、元のパスの拒否ポリシーもスナップショットに及び、明示的なスナップショットのパスには、より厳しい制限を別途設けられます。メタデータの照会は、HDFS のディレクトリー巡回権限の意味をそのまま引き継ぎます。 +スナップショットパスは、実際に要求されたパスと `.snapshot/<スナップショット名>` を取り除いた元のパスの両方を検査します。たとえば `/data/.snapshot/s1/secret` は `/data/secret` も検査します。そのため元のパスに対する拒否ポリシーはスナップショットにも及びます。明示的なスナップショットパスにさらに厳しい制限を設定することもできます。メタデータの問い合わせは HDFS のディレクトリー走査の権限セマンティクスに従います。 -1 回の再帰的な認可で検査するのは最大 `100000` 個の inode で、上限を超える場合は NameNode 内でメモリを無限に割り当てないように操作を拒否します。極端に長いパスはパス全体でポリシーを判定し、監査でのリソース表示は `1000` 文字に制限し、リクエストの詳細に元の長さと SHA-256 ダイジェストを記録します。 +1 回の再帰的な認可で検査する inode は最大 `100000` 個で、上限を超えると操作を拒否し、NameNode 内で際限なくメモリーを確保することを防ぎます。非常に長いパスは完全なパスでポリシーを判定します。監査のリソース表示は `1000` 文字に制限され、リクエストの詳細に元の長さと SHA-256 ダイジェストを記録します。 -以下のスーパーユーザーコールバックは Hadoop 3.4 と 3.5 に適用されます。HDFS のスーパーユーザーは引き続き Hadoop が管理します。パス付きのスーパーユーザーコールバックは、まず Hadoop のスーパーユーザーチェックを通った後、Hadoop 3.4 / 3.5 が提供する操作名でポリシーを検査します。ファイルの読み取りとメタデータの照会では `read`、ディレクトリーの列挙では `read` + `execute`、既知の変更操作では `write` が必要です。未知、欠落、または正確に推定できない操作(例: `checkAccess` と `concat`)は、保守的に 3 つの権限をすべて要求します。 +Hadoop 2.7、2.10、3.2、3.3 はエージェントを呼び出す前にスーパーユーザーを素通りさせるため、エージェントはそれらのアクセスを制御も監査もできません。Hadoop 3.4、3.5 のパス付きスーパーユーザーコールバックは、先にネイティブチェックを通ったうえで操作名に応じてポリシーを検査します。ファイルの読み取りとメタデータの問い合わせには `read`、ディレクトリーの列挙には `read` + `execute`、既知の変更操作には `write` が必要です。未知、欠落、または正確に推定できない操作(`checkAccess` や `concat` など)には、保守的に 3 つの権限すべてを要求します。 -スーパーユーザーコールバックには完全な inode とサブツリーのコンテキストがなく、パスのないクラスター管理呼び出しはネイティブチェックを維持します。サブディレクトリーのポリシーでスーパーユーザーのすべての再帰操作を制限することはできません。データの利用者は一般の Hadoop ユーザーを使ってください。 +スーパーユーザーのコールバックには完全な inode とサブツリーの文脈がないため、パスを伴わないクラスター管理の呼び出しはネイティブチェックのままです。サブディレクトリーのポリシーでスーパーユーザーのすべての再帰操作を制限することはできません。データを使う人は一般の Hadoop ユーザーを使ってください。 -## 指標 +## メトリクス -エージェントは Hadoop の Metrics2 の仕組みを通じて指標を報告し、NameNode 自身の dfs 指標と同じ sink を使います。NameNode の JMX では `Hadoop:service=NameNode,name=GrantForgeHdfsAgent` として現れ、Prometheus の JMX エクスポーターがそのまま取得できます。すべての指標は `instance`(データサービスのインスタンス名)と `agentVersion` のタグを持つため、HA の 2 つの NameNode を区別して確認できます。指標の登録に失敗しても失うのは指標だけで、エージェントは警告を記録し、指標なしで認可を続行します。 +エージェントは Hadoop の Metrics2 でメトリクスを報告し、NameNode 自身の dfs メトリクスと同じ sink を使います。NameNode の JMX では `Hadoop:service=NameNode,name=GrantForgeHdfsAgent` として現れます(Prometheus の JMX エクスポーターでそのまま収集できます)。各メトリクスには `instance`(データサービスのインスタンス名)と `agentVersion` のラベルが付くため、HA の 2 台の NameNode を別々に確認できます。メトリクスの登録に失敗しても失われるのはメトリクスだけで、エージェントは警告を記録し、メトリクスなしで認可を続けます。 -| 指標 | 説明 | +| メトリクス | 説明 | | --- | --- | | `Callbacks` | エージェントが実行した認可コールバックの数 | -| `SuperuserCallbacks` | エージェントが実行したスーパーユーザーコールバックの数 (3.4 / 3.5) | +| `SuperuserCallbacks` | エージェントが実行したスーパーユーザーコールバックの数 | | `NativeDenies` | エージェントより前に Hadoop が拒否したアクセスの数 | -| `EvaluationFailures` | ポリシーの評価の例外によりフェイルクローズしたコールバックの数 | -| `DecisionsAllowed` / `DecisionsDenied` / `DecisionsUndetermined` | ポリシーが許可 / 拒否 / 未決と判定した権限の数。未決は厳格モードでも同様に拒否します | -| `MissingSnapshots` | 検証済みのポリシースナップショットがない間に対処したコールバックの数 | -| `SnapshotVersion` | 現在使っているポリシースナップショットのバージョン。なければ 0 | -| `QueuedEvents` / `DroppedEvents` | メモリ上で報告を待っている監査イベントの数 / キューまたはディスクバッファが満杯で破棄されたイベントの数 | -| `ServerReachable` | 最後のポリシーサーバーへのアクセスが成功したかどうか(1/0) | +| `EvaluationFailures` | ポリシー評価の例外によりフェイルクローズしたコールバックの数 | +| `DecisionsAllowed` / `DecisionsDenied` / `DecisionsUndetermined` | ポリシーが許可 / 拒否 / 未決定と判断した権限の数。未決定も厳格モードでは拒否されます | +| `MissingSnapshots` | 検証済みのポリシースナップショットがない状態で処理したコールバックの数 | +| `SnapshotVersion` | 現在使っているポリシースナップショットのバージョン。0 はなしを表します | +| `QueuedEvents` / `DroppedEvents` | メモリー内で報告待ちの監査イベント数 / キューまたはディスクバッファーが満杯で破棄したイベント数 | +| `ServerReachable` | 直近のポリシーサーバーへのアクセスが成功したか(1/0) | ## デプロイ -1. GrantForge のデータサービスに `hdfs` サービスを追加し、設定を保存して接続をテストします。実際の Hadoop の短いユーザー名、ユーザーグループ、またはロールにパスポリシーを設定します。 -2. 「データ権限 → エージェント」でこのサービスのトークンを発行します。トークンの原文を各 NameNode のローカルファイルに書き込みます。たとえば `/etc/hadoop/grantforge/token` とし、NameNode を実行するユーザーが読み取れるようにします。 -3. クラスターの Hadoop 系列に合わせて、`agents/hdfs//` の `grantforge-agent-hdfs--.jar` を NameNode のクラスパスにコピーします。例:`$HADOOP_HOME/share/hadoop/hdfs/lib/`。対応するアダプターを 1 つだけ配置してください。jar は共有ロジック、Jackson、署名ライブラリを含み、Hadoop のクラスは NameNode が提供します。 -4. 各 NameNode の `hdfs-site.xml` に次のプロパティを設定します。HA の 2 つの NameNode は、異なる `instance` とそれぞれローカルのキャッシュディレクトリーを使います。 +1. GrantForge のデータサービスに `hdfs` サービスを追加し、設定を保存して接続をテストします。実際の Hadoop の短いユーザー名、ユーザーグループ、ロールに対してパスのポリシーを設定します。 +2. 「データ権限 → エージェント」でこのサービスのトークンを発行します。トークンの原文を各 NameNode のローカルファイル(例: `/etc/hadoop/grantforge/token`)に書き込み、NameNode の実行ユーザーが読めるようにします。 +3. 上の表に従って配布パッケージの `agents/hdfs//` から対応するエージェント jar を選び、NameNode のクラスパス(例: `$HADOOP_HOME/share/hadoop/hdfs/lib/`)に置きます。エージェント jar は独自のポリシーエンジン、Jackson、署名ライブラリーを含み、Hadoop のクラスは NameNode が提供します。ハートビートのエージェントバージョンには製品バージョンとコンパイルに使った Hadoop のバージョンが含まれます。 +4. 各 NameNode の `hdfs-site.xml` に次のプロパティーを設定します。HA の 2 台の NameNode には別々の `instance` とそれぞれローカルのキャッシュディレクトリーを使います。 ```xml @@ -102,58 +100,56 @@ Hadoop 2.7、2.10、3.2、3.3 には、エージェントが利用するスー ``` -5. `dfs.permissions.enabled=true` であること、および `dfs.namenode.inode.attributes.provider.bypass.users` が空であることを確認します。認可コールバックを迂回できる設定は、エージェントの起動時に拒否します。NameNode を再起動し、その後 GrantForge のエージェントページでハートビートとポリシーのバージョンを確認します。エージェントは NameNode の既存の設定を読み取るだけで、inode のネイティブプロパティは変更しません。 +5. `dfs.permissions.enabled=true` であり、`dfs.namenode.inode.attributes.provider.bypass.users` が空であることを確認します。エージェントは認可コールバックを迂回できる設定では起動を拒否します。NameNode を再起動し、GrantForge のエージェント画面でハートビートとポリシーのバージョンを確認します。エージェントは NameNode の既存の設定を読み取るだけで、inode のネイティブ属性は変更しません。 -初回のデプロイではまず `native.fallback=true` を使い、ポリシーのスナップショットが同期され、祖先ディレクトリーの権限が整ったことを確認してから、厳格モードに切り替えることができます。トークンがバインドしているサービスタイプは必ず `hdfs` である必要があります。設定が誤っている場合や、他のサービスタイプにバインドされている場合は、アクセスが拒否されます。 +初回のデプロイでは、まず `native.fallback=true` で始め、ポリシースナップショットが同期されたことを確認し、祖先ディレクトリーの権限を補ってから厳格モードに切り替えられます。トークンに紐付くサービス種別は `hdfs` でなければなりません。設定の誤りや別のサービス種別への紐付けはアクセス拒否になります。 -## 任意の設定 +## オプション設定 -| プロパティ | 既定値 | 用途 | +| プロパティー | 既定値 | 用途 | | --- | --- | --- | -| `grantforge.hdfs.connect.timeout.ms` | `5000` | GrantForge に接続するときのタイムアウト | -| `grantforge.hdfs.read.timeout.ms` | `8000` | 応答を読み取るときのタイムアウト | -| `grantforge.hdfs.refresh.interval.ms` | `30000` | サーバーに到達できないときのポリシー更新間隔。少なくとも `1000`。通常のハートビートはサーバーが提案した間隔を使います | -| `grantforge.hdfs.signing.key.file` | 未設定 | 任意の署名用公開鍵ファイル。内容はコンソールが提供する Base64 の X.509 公開鍵です。設定すると、その公開鍵の署名のみを受け入れます | -| `grantforge.hdfs.audit.batch.size` | `500` | 1 回の報告での最大イベント数。範囲は `1..1000` | -| `grantforge.hdfs.audit.queue.capacity` | `10000` | メモリ上の監査キューの容量。範囲は `1..1000000` で、少なくとも 1 バッチ分は保持できる必要があります。キューが満杯になると、新しいイベントを数えて破棄します | -| `grantforge.hdfs.audit.flush.interval.ms` | `5000` | 監査のフラッシュ間隔。正の整数で、最大 `2147483647`。小さくすると報告の遅延を抑えられます | -| `grantforge.hdfs.audit.spool.limit.bytes` | `67108864` | サーバーに到達できないときのディスクバッファの上限。0 以上の整数。`0` でディスクバッファを無効にします | +| `grantforge.hdfs.connect.timeout.ms` | `5000` | GrantForge への接続のタイムアウト | +| `grantforge.hdfs.read.timeout.ms` | `8000` | 応答の読み取りのタイムアウト | +| `grantforge.hdfs.refresh.interval.ms` | `30000` | サーバーに到達できないときのポリシー更新間隔。最小 `1000`。通常のハートビートはサーバーが推奨する間隔を使います | +| `grantforge.hdfs.signing.key.file` | 未設定 | 任意の署名公開鍵ファイル。内容はコンソールが提供する Base64 の X.509 公開鍵で、設定するとこの鍵の署名だけを受け入れます | +| `grantforge.hdfs.audit.batch.size` | `500` | 1 回に報告する最大イベント数。範囲は `1..1000` | +| `grantforge.hdfs.audit.queue.capacity` | `10000` | メモリー内の監査キューの容量。範囲は `1..1000000` で、少なくとも 1 バッチを収められる必要があります。満杯になると数を数えて新しいイベントを破棄します | +| `grantforge.hdfs.audit.flush.interval.ms` | `5000` | 監査のフラッシュ間隔。正の整数で最大 `2147483647`。小さくすると報告の遅延が減ります | +| `grantforge.hdfs.audit.spool.limit.bytes` | `67108864` | サーバーに到達できないときのディスクバッファーの上限。0 以上の整数で、`0` はディスクバッファーを無効にします | -アクセス量が急に多くなったときは、監査キューを大きくしてキューのあふれを減らし、フラッシュ間隔を短くすると監査の遅延は減りますが、報告の頻度は増えます。ディスクバッファの上限は、長時間の切断中のディスク使用量を抑えるものであり、バッファを無効にしたり使い切ったりするとイベントを失う可能性があります。監査の報告はバックグラウンドで実行され、ポリシーサーバーの応答は待ちません。 +アクセスが急増する場合は監査キューを大きくするとキューのあふれが減ります。フラッシュ間隔を短くすると監査の遅延は下がりますが、報告の頻度は上がります。ディスクバッファーの上限は長時間の切断時のディスク使用量を抑えるためのもので、無効にしたり使い切ったりするとイベントが失われることがあります。監査の報告はバックグラウンドで行われ、ポリシーサーバーの応答を待ちません。 -署名用公開鍵を設定していない場合、エージェントは初回にサーバーへアクセスしたときに公開鍵を取得し、スナップショットと一緒に保存します。エージェントは Hadoop が渡した短いユーザー名とユーザーグループを使い、ロールと追加のグループは署名付きスナップショットから取得します。Kerberos principal の短い名前へのマッピングは、クラスターの `hadoop.security.auth_to_local` が決めます。 +署名公開鍵を設定していない場合、エージェントは最初にサーバーから公開鍵を取得し、スナップショットと一緒に保存します。エージェントは Hadoop から渡される短いユーザー名とユーザーグループを使い、ロールと追加のグループは署名付きスナップショットから取得します。Kerberos principal から短い名前への対応はクラスターの `hadoop.security.auth_to_local` で決まります。 ## ソースからのビルドと検証 -以下は 3.5 の例です。対象アダプターに合わせて 2.7、2.10、3.2、3.3、3.4 に置き換えてください。 - ```sh ./mvnw -pl agents/grantforge-agent-hdfs-3.5 -am package ./mvnw -pl plugins/grantforge-plugin-hdfs,agents/grantforge-agent-hdfs-3.5,core/grantforge-plugin-host -am test ``` -エージェントの成果物は `agents/grantforge-agent-hdfs-3.5/target/grantforge-agent-hdfs-3.5-.jar` として生成されます。単体テストは NameNode の認可コールバック、設定、バージョンのメタデータ、ポリシーの判定を扱い、WebHDFS と Kerberos のテストはローカルの一時的なサービスを起動します。本番投入前には、対象のクラスターでも読み取りと書き込み、作成、名前変更、再帰的な削除、HA の切り替え、切断後のキャッシュの動作を検証する必要があります。 +エージェントの成果物は `agents/grantforge-agent-hdfs-3.5/target/grantforge-agent-hdfs-3.5-.jar` にあります。ユニットテストは NameNode の認可コールバック、設定、バージョンのメタデータ、ポリシーの判断をカバーし、WebHDFS と Kerberos のテストはローカルに一時的なサービスを起動します。本番投入の前に、対象クラスターで読み書き、作成、名前変更、再帰削除、HA の切り替え、切断後のキャッシュの動作も検証してください。 -統合の検証は `verify` フェーズで Testcontainers を使って実行します(単体テストはクラスターを起動しません。`verify` には利用可能な Docker daemon が必要です)。 +統合検証は `verify` フェーズで Testcontainers を使って実行されます(ユニットテストはクラスターを起動しません。`verify` には利用可能な Docker デーモンが必要です)。 ```sh ./mvnw -pl agents/grantforge-agent-hdfs-3.5 -am verify -# nightly と同じエントリーポイントを使います -bash script/ci/hdfs_integration.sh 3.5 -# 単一系列または全バージョンの検証 -bash script/ci/hdfs_integration.sh all +# nightly と同じ入口 +bash script/ci/hdfs_integration.sh +# 指定したバージョン系列だけを検証 +bash script/ci/hdfs_integration.sh 2.10 ``` -テストはバージョンが固定された Apache Hadoop のコンテナイメージを使い、実際にパッケージングしたエージェント jar を NameNode のクラスパスに入れます。Testcontainers は分離されたネットワークを作成し、NameNode と DataNode のライフサイクルを管理します。検証するのは、読み取りと書き込み、作成、追加、名前変更、削除、再帰とスナップショットの拒否、ネイティブ権限と監査、ポリシーの更新、ポリシーサーバーを切断した後に署名付きキャッシュで NameNode を再起動する場合、スナップショットがないときの厳格モードとネイティブ権限へのフォールバックです。 +テストはバージョンを固定した Apache Hadoop のイメージを使います。古いバージョンのイメージは、ダイジェストを固定した Java 8 のベースイメージと SHA-512 で検証した Apache の配布物からビルドします。対応するバージョンの実際のエージェント jar を NameNode のクラスパスに置き、実際の Hadoop と JVM のバージョンをアサートします。Testcontainers は分離されたネットワークを作成して NameNode と DataNode のライフサイクルを管理し、読み書き、作成、追記、名前変更、削除、再帰とスナップショットの拒否、ネイティブ権限と監査、ポリシーの更新、ポリシーサーバーから切断したあと NameNode を再起動して署名付きキャッシュを使うこと、スナップショットがないときの厳格モードとネイティブ権限へのフォールバックを検証します。 -実行中の Docker daemon が必要で、テストイメージをダウンロードできる必要があります。Docker を利用できない場合、テストは失敗し、黙ってスキップされることはありません。ファイルシステムクライアントは Hadoop コンテナの内部で実行され、ポリシーの HTTP サービスは Testcontainers のホストポートフォワーディングを使うため、外部の Hadoop クラスターは不要です。テストの終了後、コンテナとテストネットワークを片付け、ログは `agents/grantforge-agent-hdfs-3.5/target/hdfs-testcontainers` に保存します。 +実行中の Docker デーモンと、テストイメージのダウンロードの許可が必要です。Docker が使えない場合、テストは黙ってスキップされずに失敗します。ファイルシステムのクライアントは Hadoop コンテナーの中で実行され、ポリシーの HTTP サービスには Testcontainers のホストポート転送を使うため、外部の Hadoop クラスターは不要です。テストの終了後にコンテナーとテストネットワークを片付け、ログは `agents/grantforge-agent-hdfs-3.5/target/hdfs-testcontainers` に保存します。 -HA のテストは NameNode 2 台、DataNode 1 台、JournalNode 1 台を起動し、2 つのエージェントにそれぞれ個別のインスタンス名とキャッシュディレクトリーを設定します。論理 HDFS クライアントでアクティブノードを手動で切り替え、切り替え後の読み取りと書き込み、拒否ポリシーを検証します。JournalNode 1 台はテスト用であり、過半数の障害耐性は検証せず、ZooKeeper の自動フェイルオーバーにも関係しません。 +HA テストは NameNode 2 台、DataNode 1 台、JournalNode 1 台を起動し、2 つのエージェントに別々のインスタンス名とキャッシュディレクトリーを設定します。論理 HDFS クライアントでアクティブなノードを手動で切り替え、切り替え後の読み書きと拒否ポリシーを検証します。JournalNode 1 台はテスト専用で、過半数による耐障害性は検証せず、ZooKeeper による自動フェイルオーバーも扱いません。 -自動 HA テスト(Hadoop 2.10〜3.5、Java 17 と 21)は ZooKeeper、JournalNode 3 台、それぞれ ZKFC を持つ NameNode 2 台、DataNode 1 台を起動します。アクティブな NameNode を強制停止すると ZooKeeper がもう一方に引き継がせ、引き継いだノードがポリシーを適用し続け、拒否が自身のインスタンスに記録されること、NameNode の不在中に公開したポリシーが Standby として復帰した時点で適用され、役割を戻した後も古いバージョンに戻らず適用されること、ポリシーサーバーが使えない間は引き継いだ NameNode が保持するスナップショットで適用を続け、サーバー復帰後に監査を送ること、JournalNode 3 台のうち 1 台停止では書き込みが成功し、2 台停止では書き込みが失敗してアクティブな NameNode がクォーラムなしで続行せず停止することを検証します。Kerberos 版(Hadoop 3.5.0 のみ)では、JournalNode も keytab でサインインして HTTPS のみを提供し、ZKFC は SASL で ZooKeeper に認証し、選挙用の znode は NameNode の principal だけが使えます(認証されていない ZooKeeper クライアントは読み取りもできません)。アクティブな NameNode を停止するともう一方が引き継いで Kerberos ユーザーにポリシーを適用し続けること、チケットのないクライアントが常に拒否されること、最初の NameNode が戻ると役割を戻せることを検証します。JournalNode は NameNode と同じ短縮名の principal で動作します。JournalNode は edits を NameNode の完全な principal か自分と同じ短縮名の要求者にしか渡さず、HTTP 認証は短縮名しか渡さないためです。Hadoop 2.7 の自動 HA は未検証です。そのクライアントはすべての NameNode のホスト名を解決できる間しか切り替えず、停止したコンテナーの名前は解決できなくなるため、もう一方の NameNode を試さずに UnknownHostException で終了します。 +自動 HA テスト(Hadoop 2.10〜3.5、Java 17 と 21)は ZooKeeper、JournalNode 3 台、それぞれ ZKFC を持つ NameNode 2 台、DataNode 1 台を起動します。テストでは次を検証します。アクティブな NameNode を直接停止すると ZooKeeper が別のノードを選んで引き継ぎ、新しいアクティブノードがポリシーを引き続き適用し、拒否イベントはそのノードのインスタンスに帰属すること。ノードが離れている間に公開された新しいポリシーは Standby として復帰した後に有効になり、切り戻した後も古いバージョンに戻らず新しいバージョンを適用すること。ポリシーサービスが使えない間は、引き継いだノードが読み込み済みのスナップショットで適用を続け、サービスの復旧後に監査を再送すること。JournalNode 3 台のうち 1 台を止めても書き込みは成功し、2 台を止めると書き込みが失敗してアクティブな NameNode が停止し、過半数がないまま続行しないこと。さらに Kerberos 版(Hadoop 3.5.0 のみ)もあります。JournalNode も keytab でログインして HTTPS だけを公開し、ZKFC は SASL で ZooKeeper に認証し、選出用のノードには NameNode の principal だけがアクセスできます(認証していない ZooKeeper クライアントは読み取りさえ拒否されます)。テストでは、アクティブな NameNode を止めると別のノードが引き継いで Kerberos ユーザーへのポリシー適用を続け、チケットのないクライアントは常に拒否され、元のノードが復帰すると切り戻せることを検証します。JournalNode は NameNode と同じ短い名前の principal で動かします。JournalNode は edits を NameNode の完全な principal か、自分と同じ短い名前の要求元にしか渡さず、HTTP の認証では短い名前しか得られないためです。Hadoop 2.7 の自動 HA は検証していません。そのクライアントはすべての NameNode のホスト名を解決できるときにしか切り替えず、テストで停止したコンテナーの名前は解決できなくなるため、クライアントは別のノードを試さずに UnknownHostException を報告します。 -Kerberos テストは 6 つのバージョンラインすべてで実行します(Java 17 と 21)。KDC はテスト JVM 内で動き、NameNode と DataNode はそれぞれの keytab を使ってセキュアモードで起動し、DataNode は SASL を経たデータ転送と HTTPS のみを提供し、WebHDFS は SPNEGO を使います。principal が短い名前に対応付けられたうえで GrantForge のポリシーが読み書きに適用されること、ポリシーのないユーザーが拒否されること、拒否が短い名前で監査されること、チケットのないクライアントが simple 認証に戻らずに拒否されること、再起動した NameNode が再度サインインしてポリシーを適用し続けることを検証します。 +Kerberos テストは 6 つのバージョン系列すべてで実行されます(Java 17 と 21)。KDC はテストの JVM 内で動き、NameNode と DataNode はそれぞれ keytab を使ってセキュアモードで起動します。DataNode のデータ転送には SASL が必要で HTTPS だけを公開し、WebHDFS は SPNEGO を使います。テストでは、principal を短い名前に対応付けたうえで GrantForge のポリシーが読み書きに効くこと、ポリシーのないユーザーが拒否されること、拒否イベントが短い名前で監査されること、チケットのないクライアントが拒否され simple 認証にフォールバックしないこと、NameNode の再起動後に再ログインしてポリシーの適用を続けることを検証します。 -共有のネイティブ単体テストは `agents/grantforge-agent-hdfs-native/src/test` で実行し、6 つのアダプターにはコンパイルしません。バージョン固有のコールバックのテストは各番号付きモジュールに残します。共有のコンテナーテストのソースは引き続き `agents/grantforge-agent-hdfs-common/src/test/shared` に置き、番号付きの本番モジュールにコンパイルします。テスト専用 Maven プロジェクトは作成せず、Testcontainers はテスト依存関係に限定します。nightly は Java 17/21 のテストホストで 6 つの Hadoop バージョンを検証し、コンテナー内部の Java は上記の表に従います。レポートとログを保存します。 +テストのソースは実際の本番モジュールの `src/test` にあります。共通のポリシーのユニットテストは common で、共通のネイティブのユニットテストは `agents/grantforge-agent-hdfs-native/src/test` で実行され、6 つのバージョン別エージェントにはコンパイルされません。バージョンごとのコールバックのテストは対応する番号付きモジュールに残り、共通のコンテナーテストのソース `agents/grantforge-agent-hdfs-common/src/test/shared` は引き続き各番号付きモジュールにコンパイルされます。独立した Maven のテストプロジェクトはありません。Testcontainers は test スコープだけです。nightly は Java 17 と 21 のホストで 6 つの Hadoop バージョンを検証し、Hadoop のコンテナーには上の表の JVM を使います。現在のコンテナーは Simple 認証、手動 HA、6 つすべてのバージョン系列での Kerberos と HTTPS、2.10〜3.5 の自動 HA、Hadoop 3.5.0 での Kerberos 下の自動 HA をカバーしています。2.7 の自動 HA、ほかのバージョンでの Kerberos 下の自動 HA、ベンダーのパッチは対象環境での検証が必要です。 -Hadoop の拡張エントリーポイントと権限の意味については、[Apache Hadoop 3.5.0 API](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/build/source/hadoop-hdfs-project/hadoop-hdfs/target/api/org/apache/hadoop/hdfs/server/namenode/INodeAttributeProvider.html) と [HDFS 権限ガイド](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/HdfsPermissionsGuide.html) を参照してください。 +Hadoop の拡張ポイントと権限のセマンティクスは [Apache Hadoop 3.5.0 API](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/build/source/hadoop-hdfs-project/hadoop-hdfs/target/api/org/apache/hadoop/hdfs/server/namenode/INodeAttributeProvider.html) と [HDFS 権限ガイド](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/HdfsPermissionsGuide.html) を参照してください。 diff --git a/docs/content/ja/guide/catalog.md b/docs/content/ja/guide/catalog.md index 3270f0a0..8a91c8e8 100644 --- a/docs/content/ja/guide/catalog.md +++ b/docs/content/ja/guide/catalog.md @@ -22,6 +22,12 @@ description: アプリケーションとリソースツリー、リソースの - **OAuth クライアント**:業務アプリケーション用のクライアントを登録します。[OAuth 2.1 と OpenID Connect](/ja/integration/oauth/) を参照してください。 - **フィールド**:フィールドを選択すると、そのフィールドがどのインターフェースに現れるか(返すか受け取るか)を表示します。 +リソースがあるアプリケーションのカタログで **エクスポート** をクリックすると、開発や AI ツールの参考になる JSON ファイルをダウンロードできます。ファイルにはアプリケーション情報、階層ごとに整理された全リソースとリソースの依存関係が含まれ、依存関係の両端のリソースコードも付きます。検索やフィルターはエクスポート内容に影響しません。`formatVersion` はエクスポート形式のバージョンを示します。 + +対象のアプリケーションを選んで **インポート** をクリックし、バージョン 1 のエクスポートファイルをアップロードしてまず事前チェックします。インポートは同じアプリケーション内で種類と親が一致するリソースだけをリソースコードで再利用し、既存の設定は上書きしません。種類、親、依存関係の種類が衝突するとインポート全体が適用されません。新しいリソースと依存関係は確認後にまとめて書き込まれ、同じファイルを再びインポートしても重複は作られません。組み込みアプリケーションはインポートに対応せず、宣言された依存関係はインポート後に手動の依存関係になります。リソースのインポートにはリソース作成権限が、依存関係のインポートにはさらにリソース更新権限が必要です。 + +エクスポートに含まれるのは GrantForge に登録された情報だけです。ページなどのリソースの `route` はフロントエンドのルート、API リソースの `code` は権限コードです。外部の業務 API の HTTP メソッドとリクエストパスは、登録されていなければ自動では推測しません。JSON はエクスポート時点のスナップショットで、どのユーザーの付与結果も含まないため、リソースが変わったら再度エクスポートしてください。 + ## API カタログ **プラットフォーム管理 → API カタログ** はサーバー起動時に自動登録されたすべてのインターフェースと、それぞれのアクセス要件を列挙します。アクセス要件は、公開、ログインのみで利用可能、特定の権限コードが必要、のいずれかです。権限が必要なインターフェースは権限コードごとにリソースカタログに分類され、ロールに権限を付与する際にこれらの権限を参照します。インターフェースの追加、廃止、または権限コードの変更時には「確認待ちの変更」として一覧表示され、確認するとカタログが更新されます。 diff --git a/docs/content/ja/guide/explain.md b/docs/content/ja/guide/explain.md index 18ca2cf3..fc7782f8 100644 --- a/docs/content/ja/guide/explain.md +++ b/docs/content/ja/guide/explain.md @@ -13,6 +13,8 @@ description: ある人が何をできてその理由は何なのかを確認し **ユーザー管理** のユーザー行で「有効な権限を表示」をクリックすると、そのユーザーが今使えるすべてを確認できます。ロール、メニューとボタン、API、データの範囲、制限されたフィールドです。 +アプリケーションを選ぶと、そのアプリケーションの有効なリソースと API 権限を確認できます。「理由」はロールによる付与とユーザーへの直接付与の出所を示します。ロール、データ範囲、フィールド権限はユーザー全体の設定を表示します。ロールの追加や削除をシミュレーションしても、既存の直接付与は保持されます。 + ![有効な権限](/screenshots/user-permissions.png) いずれかの項目の **説明** をクリックすると、GrantForge は「なぜ使えるのか」を答えます。どのロールから、どの割り当て(直接、ユーザーグループ、部門、職位)とどのリソースを経由して導出されたかです。使えない場合は、権限付与を受けていないのか、それとも何らかの拒否に遮られているのかを説明します。 diff --git a/docs/content/ja/guide/identity-sources.md b/docs/content/ja/guide/identity-sources.md index ad2d1377..a3f654af 100644 --- a/docs/content/ja/guide/identity-sources.md +++ b/docs/content/ja/guide/identity-sources.md @@ -44,6 +44,11 @@ description: 会社のディレクトリ(LDAP/AD)や OpenID Connect プロ | クライアント ID / シークレット | プロバイダーに登録したクライアントです。シークレットがない場合は公開クライアントとして動作し、PKCE を使用します | | コールバックアドレス | ページに表示される `/api/v1/auth/federated/callback/<エンコーディング>` のアドレスで、プロバイダーのクライアントに登録する必要があります | | スコープとクレーム | 既定値は `openid profile email` です。ユーザー名、表示名、メールアドレスはそれぞれ `preferred_username`、`name`、`email` クレームから取得します | +| 電話番号クレーム | 既定では `phone`(Casdoor)を読み取ります。ほかのプロバイダーでは `phone_number` を設定できます。電話番号を同期するには scope に `phone` を含めます。ログイン時にプロバイダーが番号を返さない場合は既存の番号を保持します | +| アバター URL | OIDC 標準の `picture` クレームから同期します。scope に `profile` を含める必要があります。返されない場合は既存のアバター URL を保持します | +| 認可リクエストの追加パラメーター | 任意の JSON オブジェクトで、値はすべて文字列にします。たとえば Casdoor では `{"provider_hint":"github"}` で設定済みのプロバイダーを指定できます。パラメーターはブラウザーのアドレスに表示されるため、シークレットは入れないでください | + +プロトコルの主要パラメーター(`state`、`nonce`、`redirect_uri`、PKCE パラメーターを含む)は GrantForge が管理し、追加パラメーターで上書きすることはできません。 有効にすると、ログインページに「<名前> でログイン」ボタンが表示されます。ユーザーはプロバイダーでログインしたあと GrantForge に戻り、二段階認証を有効にしたアカウントは認証コードの入力も必要です。 @@ -54,3 +59,26 @@ description: 会社のディレクトリ(LDAP/AD)や OpenID Connect プロ - アカウントを自動作成しないアイデンティティソースでは、既に関連付けられたユーザーのみがログインできます。 - アイデンティティソースを無効にすると、そのユーザーはログインできません。アカウントが使用中のアイデンティティソースは削除できません。 - アイデンティティソースのアカウントには、通常どおりロールを割り当て、二段階認証を有効にできます。 + +組織構造の同期は独立したデータソースで設定し、OIDC ログインの設定を変える必要はありません。 + +## データソースと組織構造の同期 + +データソースは組織構造の同期を担い、アイデンティティソースは引き続き OIDC または LDAP のログインを担います。両者は別々に設定し、データソースを無効にしても OIDC ログインには影響しません。 + +1. 「アクセス制御 → アイデンティティソース」で既存の OpenID Connect ログインを設定します。 +2. 「アクセス制御 → データソース」で DingTalk のデータソースを追加し、関連付ける OIDC アイデンティティソースを選びます。 +3. CorpId、AppKey、AppSecret と起点の部署 ID(通常は `1`)を入力します。AppSecret は個別に暗号化して保存され、API が平文を返すことはありません。編集時に空欄のままにすると元の値を保持します。 +4. ローカルの部署の下にマウントできます。同期間隔の既定値は 30 分で、15–10080 分を指定できます。空欄にすると手動同期のみになります。 +5. 保存後に接続をテストし、初回同期を実行します。DingTalk アプリケーションには部署と従業員の連絡先を読み取る権限が必要で、その許可範囲は選んだサブツリーを含んでいなければなりません。 +6. ユーザーが SSO でログインすると、信頼できる OIDC のメールアドレスで従業員を照合し、部署と主所属部署を自動で設定します。メールアドレスは管理者が管理し、従業員が他人になりすまして使えないものであり、DingTalk のメールアドレスと一致している必要があります。従業員のメールアドレスがない場合は企業メールアドレスを使います。 + +ログインはローカルの連絡先スナップショットだけを参照し、DingTalk にはリクエストしません。MFA が有効な場合は、2 段階目の認証に成功してから所属を更新します。初回同期より前にログインしたアカウントは信頼できるメールアドレスを一時保存し、同期完了後に関連付けます。ローカルで編集できるプロフィールのメールアドレスは照合に使いません。欠落している、重複している、または別のアカウントに紐付け済みのメールアドレスからは新しい紐付けを作りません。 + +従業員との紐付けは外部の従業員 ID で固定され、メールアドレスが変わっても別の従業員に移ることはありません。同期は複数部署への所属も扱います。主所属部署は有効な元の主所属部署を優先して残し、なければ安定した部署 ID の順で選びます。ローカルの部署、ユーザーグループ、ポジション、直接のロール付与は保持されます。同期された部署にロールを割り当てると、ユーザーは既存の付与ルールに従ってロールを継承し、異動すると権限も更新されます。 + +組織スナップショットや関連付け待ちのアカウントができた後は、関連付けたアイデンティティソース、企業、アプリケーション、起点の部署、マウント先を変更できません。同期された部署と所属は読み取り専用で、ユーザー管理や組織管理から手動で上書きすることはできません。消えた部署は既存の付与を守るために残します。従業員が許可された連絡先から外れると、このデータソースが管理する所属を外しますが、アカウントを自動で無効にはしません。同期された部署を持つデータソースは直接削除できず、代わりに無効にできます。 + +同期はすべてのページを取得してからアトミックに適用します。読み取りの失敗、階層の異常、許可範囲の変化があった場合は前回成功したスナップショットを保持し、管理者は同期の概要、異常の一覧、監査ログで結果を確認できます。許可範囲が変わった場合は、元の承認済み範囲に戻してから同期してください。 + +現在は [DingTalk 公式のサーバー SDK](https://open.dingtalk.com/document/development/download-the-server-side-sdk) を使い、HTTP 呼び出しを独自に実装していません。プロバイダーのアダプターは共通の部署と従業員のスナップショットを出力するため、今後 Feishu のアダプターを追加しても同じ同期、紐付け、権限更新のロジックを再利用できます。 diff --git a/docs/content/ja/guide/users.md b/docs/content/ja/guide/users.md index 383efb3e..e23c08b1 100644 --- a/docs/content/ja/guide/users.md +++ b/docs/content/ja/guide/users.md @@ -31,6 +31,7 @@ description: アカウントの作成、検索、編集、無効化、ロック | 二段階認証のリセット | 認証器を紛失した場合に使用します。そのアカウントの二段階認証を無効にし、セッションを終了します | | ロールの表示 | アカウントが保持するロールと、各ロールの由来(直接割り当て、ユーザーグループ、部門、職位) | | 有効な権限の表示 | [権限の説明、シミュレーションと監査](/ja/guide/explain/) を参照してください | +| 直接付与 | 業務アプリケーションを選び、そのユーザーだけにメニュー、ページ、ボタン、API を許可します。有効期限も設定できます | | 削除 | アカウントと部門の関連を削除します。監査記録は保持されます | システムアカウント(初期化時に作成される管理者)と自分のアカウントは、無効化、ロック、削除できません。 @@ -38,3 +39,5 @@ description: アカウントの作成、検索、編集、無効化、ロック ## セルフサービス登録 デフォルトは無効です。`grantforge.security.registration-enabled=true` を設定すると、ログインページに「アカウントの作成」という入口が表示され、登録されたアカウントは `grantforge.security.registration-tenant` で指定したテナントに入り、どのロールも持たない一般アカウントになります。 + +直接付与はロールの権限と合わせて計算され、ロールの拒否が引き続き優先し、リソースの依存関係は既存のルールどおりに導かれます。直接付与はロールに基づく職務分離の制約を受けず、データ行やフィールドのポリシーは付与せず、コンソールアプリケーションには使えません。操作できるのは、ロールを付与する権限を持ち、対象ユーザーを変更できる管理者だけです。 diff --git a/docs/content/ja/integration/javascript.md b/docs/content/ja/integration/javascript.md index 776d2cc4..f9292f84 100644 --- a/docs/content/ja/integration/javascript.md +++ b/docs/content/ja/integration/javascript.md @@ -48,7 +48,8 @@ app.use(createGrantForge(grantForge)) ```vue - ``` + +``` `useGrantForge()` は `authorization`、`error`、`refresh()`、`can()`、`hasResource()` を提供します。ボタンを非表示にするのは利便性にすぎず、アプリケーションの API も同じ権限を必ず検証する必要があります。 diff --git a/docs/content/ja/start/quick-start.md b/docs/content/ja/start/quick-start.md index e8da935d..48fd5481 100644 --- a/docs/content/ja/start/quick-start.md +++ b/docs/content/ja/start/quick-start.md @@ -38,7 +38,7 @@ WARN ... First-run setup is pending. Open the console and enter this setup toke ## 2. 初期化の完了 -ブラウザで http://127.0.0.1:9999/ を開くと、コンソールは自動的に初期化ページへ移動します。ログに出力されたトークン、組織名、最初の管理者のユーザー名とパスワード(12 文字以上)を入力します。 +ブラウザで を開くと、コンソールは自動的に初期化ページへ移動します。ログに出力されたトークン、組織名、最初の管理者のユーザー名とパスワード(12 文字以上)を入力します。 ![初期設定とログインページ](/screenshots/login.png) diff --git a/docs/content/ko/changelog/2026.4.0.md b/docs/content/ko/changelog/2026.4.0.md new file mode 100644 index 00000000..cd01e374 --- /dev/null +++ b/docs/content/ko/changelog/2026.4.0.md @@ -0,0 +1,68 @@ +--- +title: "2026.4.0" +description: "GrantForge 2026.4.0(2026-11-08): Elasticsearch, Apache Doris, MongoDB, Redis/Valkey 서비스 플러그인과 에이전트, DingTalk 조직 구조 동기화, 사용자 직접 권한 부여, 리소스 카탈로그 내보내기와 가져오기, 모든 언어의 번역이 완전한지 검사하는 CI." +--- + + +2026.4.0은 외부 시스템 권한을 Elasticsearch, Apache Doris, MongoDB, Redis와 Valkey로 확장합니다. 네 에이전트 모두 시스템 옆에서 실행되며 시스템 자체의 접근 제어를 정책과 일치시킵니다. 신원과 인가 쪽에서는 DingTalk 조직 구조 동기화, 사용자 직접 권한 부여, 리소스 카탈로그 내보내기와 가져오기가 추가되었습니다. 전체 변경 사항은 [GitHub 비교](https://github.com/devlive-community/grantforge/compare/v2026.3.0...v2026.4.0)를 참고하십시오. + +## Elasticsearch + +- Elasticsearch 서비스 플러그인: 인덱스와 데이터 스트림, 그 필드, 클러스터. 비밀번호, API 키 또는 TLS 클라이언트 인증서로 로그인합니다. [Elasticsearch](/ko/plugins/elasticsearch/)를 참고하십시오. +- 클러스터 옆에서 실행되는 에이전트: 사용자마다 Elasticsearch 자체 역할을 유지해 네이티브 사용자에게 부여하고, 다른 realm의 사용자에게는 역할 매핑으로 부여합니다. 라이선스가 허용하면 필드 수준과 문서 수준 보안도 붙입니다. 인덱스 패턴은 패턴으로 유지되어 새 인덱스에도 바로 접근할 수 있습니다. [Elasticsearch 에이전트](/ko/external/elasticsearch-agent/)를 참고하십시오. +- 접근 기록은 감사 로그 파일에서 가져옵니다. 통합 테스트는 Elasticsearch 8.19, 9.3, 9.4, 9.5를 다룹니다. + +## Apache Doris + +- Doris 서비스 플러그인: 카탈로그, 데이터베이스, 테이블, 열, 워크로드 그룹. 하나 이상의 FE에 연결하고 비밀번호로 로그인합니다. [Apache Doris](/ko/plugins/doris/)를 참고하십시오. +- 클러스터 옆에서 실행되는 에이전트: 정책은 Doris 자체 역할, 열 권한 부여, 워크로드 그룹 권한 부여, 행 정책이 되어 사용자의 모든 신원에 부여됩니다. 데이터베이스나 카탈로그 전체 부여는 그로 인해 Doris가 더 많은 권한을 주지 않을 때만 합니다. [Apache Doris 에이전트](/ko/external/doris-agent/)를 참고하십시오. +- 접근 기록은 Doris의 감사 로그 테이블에서 가져옵니다. 통합 테스트는 Doris 2.1, 3.1, 4.0, 4.1을 다룹니다. + +## MongoDB + +- MongoDB 서비스 플러그인: 데이터베이스와 컬렉션. 복제본 세트나 샤딩 클러스터에 연결하고 SCRAM, 클라이언트 인증서, LDAP 또는 Kerberos로 로그인합니다. [MongoDB](/ko/plugins/mongodb/)를 참고하십시오. +- 배포 옆에서 실행되는 에이전트: 사용자마다 MongoDB 역할 하나를 유지하고 그 권한을 한 번에 교체합니다. MongoDB가 알려 주는 상속된 권한으로 GrantForge 밖의 기본 제공 역할과 사용자 정의 역할이 실제로 무엇을 주는지 보고합니다. [MongoDB 에이전트](/ko/external/mongodb-agent/)를 참고하십시오. +- 접근 기록은 Enterprise나 Percona의 감사 로그 파일 또는 프로파일러에서 가져옵니다. 통합 테스트는 MongoDB 6.0, 7.0, 8.0, 9.0을 다루며, Kerberos는 Percona Server for MongoDB에서 검증했습니다. + +## Redis와 Valkey + +- Redis 서비스 플러그인: 키, 채널, 노드. 단독 서버와 그 복제본 또는 클러스터에 연결하고 ACL 사용자로 로그인하며, TLS도 선택할 수 있습니다. [Redis](/ko/plugins/redis/)를 참고하십시오. +- 배포 옆에서 실행되는 에이전트: 모든 노드에서 각 사용자의 ACL 선택자를 정책과 일치시키고 명령 하나로 통째로 교체합니다. 루트 권한은 계속 운영자가 관리하며, 에이전트는 그 권한이 정책이 거부하는 접근을 허용하는지 `ACL DRYRUN`으로 Redis에 묻습니다. [Redis 에이전트](/ko/external/redis-agent/)를 참고하십시오. +- 거부 기록은 각 노드의 ACL 로그에서 가져옵니다. 통합 테스트는 Redis 7.2, 7.4, 8.2, 8.10과 Valkey 8.1, 9.1, 그리고 Redis 8.10 클러스터를 다룹니다. + +## 에이전트 + +- ClickHouse, PostgreSQL, OpenSearch 에이전트와 이번 릴리스에 추가된 시스템 옆 에이전트는 새 `grantforge-agent-sync` 코어를 공유합니다. 시작 프로그램, 설정, 타이머, 동기화 흐름, 감사 읽기 위치, 로그 파일 추적이 여기에 있습니다. 각 에이전트는 자기 시스템 고유의 부분만 갖고 있으며 설정과 로그 형식은 바뀌지 않았습니다. + +## 신원과 조직 + +- 데이터 소스: 조직 구조 동기화는 신원 소스와 따로 구성하며, 첫 어댑터는 공식 서버 SDK를 사용하는 DingTalk입니다. SSO로 로그인한 사용자는 신뢰할 수 있는 이메일로 부서와 주 부서에 자동 연결됩니다. 그 테이블은 지원하는 여섯 데이터베이스 모두에서, SQL Server와 Oracle을 포함해 마이그레이션됩니다. [신원 소스(LDAP와 OIDC)](/ko/guide/identity-sources/)를 참고하십시오. +- OpenID Connect 신원 소스는 휴대폰 번호와 아바타를 동기화할 수 있고, 인가 요청에 매개변수(예: Casdoor의 `provider_hint`)를 추가할 수 있습니다. 프로토콜 자체의 매개변수는 덮어쓸 수 없습니다. + +## 인가와 리소스 카탈로그 + +- 사용자 직접 권한 부여: 업무 애플리케이션의 메뉴, 페이지, 버튼 또는 API를 한 사용자에게만 허용하며 만료 시간을 지정할 수 있습니다. 역할 권한과 함께 계산되며 역할의 거부가 여전히 우선합니다. [사용자](/ko/guide/users/)를 참고하십시오. +- 리소스 카탈로그는 애플리케이션의 리소스 트리를 JSON으로 내보내 다른 애플리케이션으로 가져올 수 있습니다. 가져오기는 먼저 사전 검사를 거치며 충돌하면 아무것도 적용되지 않습니다. 플랫폼 관리자는 조건에 맞는 애플리케이션 트리를 비울 수 있습니다. [리소스 카탈로그와 API 카탈로그](/ko/guide/catalog/)를 참고하십시오. + +## 서버 + +- 시작할 때 Spring Boot 배너 대신 GrantForge 배너와 버전, Java 버전을 출력합니다. + +## 문서와 릴리스 + +- 플러그인 탭은 개요로 시작하고 시스템을 종류별로 접을 수 있는 사이드바에 묶으며, 각 시스템의 적용 방식, 행 필터와 마스킹 지원, 검증된 버전을 보여 줍니다. 각 README도 같은 순서로 구성 요소를 나열합니다. +- CI는 모든 페이지가 모든 언어로 완전히 번역되었는지 검사합니다. 문단, 제목, 표, 코드 블록, 링크가 중국어 페이지와 같아야 하고, 다른 언어의 문자가 없어야 하며, 영어 페이지에서 그대로 옮긴 문장도 없어야 합니다. README 11개도 영어 README를 기준으로 같은 검사를 받습니다. 이 검사로 찾은 누락된 번역은 모두 채웠습니다. +- 버전 검사가 모든 README의 버전 배지를 최신으로 유지합니다. +- 문서 게시와 릴리스 워크플로는 더 이상 포크에서 실행되지 않습니다. +- 서버 `grantforge-server`는 더 이상 Maven 저장소에 배포하지 않습니다. 모든 플러그인을 묶어 저장소의 크기 제한을 넘기 때문이며, 서버는 릴리스 아카이브와 이미지로 제공됩니다. + +## 수정 사항 + +- 리소스 편집 창에서 리소스 유형을 바꾸면 더 이상 맞지 않는 리소스 코드를 지웁니다. +- JavaScript 연동 가이드의 마지막 코드 블록이 뒤따르는 본문을 삼키지 않으며, Docker와 빠른 시작 페이지의 콘솔 주소가 뒤의 글자까지 링크로 만들지 않습니다. +- 보안 권고를 해결한 버전으로 의존성을 업그레이드했습니다: MariaDB Connector/J 3.5.11, OpenTelemetry 1.67.0, Guava 33.7.2, Snappy 1.1.10.9, 그리고 콘솔과 SDK의 vue-i18n, glob, minimatch. diff --git a/docs/content/ko/deploy/docker.md b/docs/content/ko/deploy/docker.md index 7bf38b53..31a24d60 100644 --- a/docs/content/ko/deploy/docker.md +++ b/docs/content/ko/deploy/docker.md @@ -43,7 +43,7 @@ docker compose -f deploy/compose/postgres.yml up -d docker compose -f deploy/compose/postgres.yml logs grantforge | grep "setup token" ``` -그 다음 http://127.0.0.1:9999/을 엽니다. 예제에서 사용하는 기본 데이터베이스 비밀번호는 체험 용도로만 적합하므로, 실제 사용 전에 `GRANTFORGE_DB_PASSWORD`로 변경하세요. MySQL 예제는 먼저 `mysql-connector-j-<버전>.jar`를 `deploy/compose/drivers/`에 넣어야 합니다. +그 다음 을 엽니다. 예제에서 사용하는 기본 데이터베이스 비밀번호는 체험 용도로만 적합하므로, 실제 사용 전에 `GRANTFORGE_DB_PASSWORD`로 변경하세요. MySQL 예제는 먼저 `mysql-connector-j-<버전>.jar`를 `deploy/compose/drivers/`에 넣어야 합니다. ## Helm diff --git a/docs/content/ko/develop/development.md b/docs/content/ko/develop/development.md index 33e44ab6..41d926c3 100644 --- a/docs/content/ko/develop/development.md +++ b/docs/content/ko/develop/development.md @@ -76,7 +76,7 @@ bash script/release/tag.sh 2026.1.0 --next 2026.2.0 # 버전을 설정하고 - `script/ci/release.sh`가 배포 패키지와 릴리스 의존성만 담은 CycloneDX SBOM, `SHA256SUMS`를 빌드합니다. - 멀티 아키텍처 이미지를 `ghcr.io/devlive-community/grantforge`에 푸시합니다. -- Maven 아티팩트(소스와 Javadoc 포함)를 GitHub Packages에 배포합니다. 저장소에 `CENTRAL_USERNAME`, `CENTRAL_PASSWORD`(Central Portal 토큰), `GPG_PRIVATE_KEY`, `GPG_PASSPHRASE`가 설정되어 있으면 서명한 뒤 Maven Central에 배포합니다. +- Maven 아티팩트(소스와 Javadoc 포함)를 GitHub Packages에 배포합니다. 저장소에 `CENTRAL_USERNAME`, `CENTRAL_PASSWORD`(Central Portal 토큰), `GPG_PRIVATE_KEY`, `GPG_PASSPHRASE`가 설정되어 있으면 서명한 뒤 Maven Central에 배포합니다. 서버 `grantforge-server`는 모든 플러그인을 묶어 저장소의 크기 제한을 넘으므로 어떤 Maven 저장소에도 배포하지 않고 릴리스 아카이브와 이미지로만 제공합니다. - GitHub Release를 생성하며, 본문은 이전 릴리스(`v*` 또는 `1.0.6` 같은 숫자 태그) 이후의 모든 커밋을 새 기능, 문제 수정, 성능 등으로 그룹화해 커밋 링크와 함께 담습니다. 릴리스 후보는 프리릴리스로 표시되며 이미지의 `latest` 태그는 갱신하지 않습니다. 로컬에서 `central` 프로파일을 켜도 기본적으로는 배포하지 않으며(`central.skip=true`), 릴리스 워크플로만 `-Dcentral.skip=false`를 명시적으로 전달합니다. @@ -100,3 +100,5 @@ pnpm build # docs/out으로 출력 ``` 페이지는 `docs/content/` 아래의 Markdown이며, 내비게이션은 `docs/lib/navigation.ts`에 정의합니다. API 레퍼런스와 오류 코드는 빌드 시 계약과 소스 코드에서 생성합니다. 스크린샷은 `script/docs/screenshots.sh`가 실제 서비스를 시작하고 예제 데이터를 넣은 뒤 Playwright로 만듭니다. + +`pnpm test`는 번역도 함께 검사합니다. 모든 중국어 페이지는 나머지 열 개 언어마다 번역이 있어야 하며, 문단, 제목 수준, 목록, 표, 코드 블록, 링크가 중국어 페이지와 같아야 합니다. 페이지에는 그 언어가 쓰지 않는 문자(번체 중국어와 일본어 속의 간체자 포함)나 영어 페이지에서 그대로 옮긴 문장이 있어서는 안 됩니다. 저장소 루트의 README 11개도 영어 README를 기준으로 같은 검사를 받으며, 이 사이트로 가는 링크는 해당 언어의 실제 페이지를 가리켜야 합니다. diff --git a/docs/content/ko/external/hdfs-agent.md b/docs/content/ko/external/hdfs-agent.md index 0a2b2242..fbd1055c 100644 --- a/docs/content/ko/external/hdfs-agent.md +++ b/docs/content/ko/external/hdfs-agent.md @@ -9,71 +9,69 @@ description: Hadoop 버전에 맞는 NameNode 에이전트로 GrantForge 경로 project root for full license text. --> -서버 플러그인 `grantforge-plugin-hdfs`가 리소스와 연결을 정의합니다. Hadoop 2.7, 2.10, 3.2, 3.3, 3.4, 3.5용 번호가 붙은 NameNode 에이전트는 해당 SPI로 접근을 검사하고 서명된 스냅샷, 정책 평가, 감사 전송 로직을 공유합니다. +서버 측 `grantforge-plugin-hdfs`가 `hdfs` 서비스 유형, 리소스, 연결 설정을 한곳에서 제공합니다. NameNode 에이전트는 Hadoop 버전별로 빌드되며, 해당 버전의 `INodeAttributeProvider`와 `AccessControlEnforcer`로 접근을 검사하고 서명된 정책, 캐시, 정책 평가, 감사 로직을 공유합니다. -공통 HDFS 로직은 `agents/grantforge-agent-hdfs-common`, 버전별 어댑터는 `agents/grantforge-agent-hdfs-`에 있습니다. 공통 네이티브 어댑터는 Java 8 / Hadoop 2.7.7 기준의 프로덕션 Maven 모듈 `agents/grantforge-agent-hdfs-native`에 있습니다. 각 번호별 모듈은 이를 Maven 바이너리 의존성으로 사용하고 버전별 진입점과 콜백을 유지하며, 공통 프로덕션 소스를 다시 컴파일하지 않습니다. `core/grantforge-agent-core`는 공통 프로토콜 및 실행 기반으로 유지됩니다. +소스에서 서버 측 플러그인은 `plugins/grantforge-plugin-hdfs`, 버전별 에이전트는 `agents/grantforge-agent-hdfs-`, Hadoop에 의존하지 않는 공통 운영 로직은 `agents/grantforge-agent-hdfs-common`에 있습니다. 공통 네이티브 어댑터는 정식 Maven 운영 모듈인 `agents/grantforge-agent-hdfs-native`에 있으며 Java 8 / Hadoop 2.7.7을 기준으로 합니다. 번호가 붙은 각 모듈은 Maven 바이너리 의존성으로 이를 참조하고 버전별 진입점과 고유 콜백만 유지하며, 공통 운영 소스를 다시 컴파일하지 않습니다. 프로토콜, 스냅숏 캐시, 감사 기반은 여전히 `core/grantforge-agent-core`에 있습니다. -서버 플러그인은 하나의 `hdfs` 서비스 타입을 유지하며 클라이언트 버전은 에이전트와 독립적입니다. Hadoop 2.x 연결과 경로 조회에는 `webhdfs://namenode:50070`를, HTTPS에는 해당 `swebhdfs://` 주소를 사용합니다. Hadoop 3.x는 RPC `hdfs://` 또는 WebHDFS를 사용할 수 있습니다. 컨테이너 테스트는 2.x의 WebHDFS와 3.x의 두 프로토콜을 검증합니다. 2.x RPC는 인증하지 않았으며 설정된 프로토콜을 자동으로 변경하지 않습니다. +서버 측 플러그인은 `hdfs` 유형 하나만 유지하며 클라이언트 버전은 에이전트 버전과 독립적입니다. Hadoop 2.x의 연결과 경로 조회에는 `webhdfs://namenode:50070`(HTTPS를 켠 경우 대응하는 `swebhdfs://` 주소)을 사용하고, Hadoop 3.x는 RPC `hdfs://`나 WebHDFS를 사용할 수 있습니다. 컨테이너 매트릭스는 2.x의 WebHDFS와 3.x의 RPC 및 WebHDFS를 검증합니다. 2.x RPC는 인증하지 않았으며 연결 프로토콜을 자동으로 전환하지 않습니다. -inode 속성 확장을 활성화한 Apache Hadoop 2.7.7에서는 일반 사용자가 `/`를 직접 조회할 때 에이전트 콜백 전에 네이티브 코드에서 null 포인터 오류가 발생합니다. 이 버전에서는 데이터 작업과 `lookup.path`에 `/data` 같은 실제 데이터 디렉터리를 사용하세요. 컨테이너 테스트는 이 루트 경로 제한도 명시적으로 검증합니다. +Apache Hadoop 2.7.7에서 inode 속성 확장을 켜면, 일반 사용자가 루트 경로 `/`를 직접 조회할 때 에이전트 콜백보다 먼저 네이티브 코드에서 널 포인터가 발생합니다. 이 버전에서는 데이터 작업과 서비스의 `lookup.path`에 `/data` 같은 실제 데이터 디렉터리를 사용하십시오. 테스트에는 루트 경로 실패에 대한 별도 단언이 남아 있습니다. -## HDFS 에이전트 대상 버전 +## 버전 선택 -| Hadoop 기준 버전 | 컨테이너 Java | 에이전트 디렉터리 | -| --- | --- | --- | -| 2.7.7 | Java 8 | `agents/hdfs/2.7/` | -| 2.10.2 | Java 8 | `agents/hdfs/2.10/` | -| 3.2.4 | Java 8 | `agents/hdfs/3.2/` | -| 3.3.6 | Java 8(amd64 이미지) | `agents/hdfs/3.3/` | -| 3.4.3 | Java 11 | `agents/hdfs/3.4/` | -| 3.5.0 | Java 17 | `agents/hdfs/3.5/` | +| 에이전트 모듈 접미사 | 검증된 Apache Hadoop | 컨테이너 JVM | 권한 인터페이스 | 슈퍼유저 경로 콜백 | +| --- | --- | --- | --- | --- | +| `2.7` | 2.7.7 | Java 8 | 매개변수 방식 | 없음 | +| `2.10` | 2.10.2 | Java 8 | 매개변수 방식 | 없음 | +| `3.2` | 3.2.4 | Java 8 | 매개변수 방식 | 없음 | +| `3.3` | 3.3.6 | Java 8 | 컨텍스트 방식 | 없음 | +| `3.4` | 3.4.3 | Java 11 | 컨텍스트 방식, 슈퍼유저 및 거부 콜백 | 있음 | +| `3.5` | 3.5.0 | Java 17 | 컨텍스트 방식, 슈퍼유저 및 거부 콜백 | 있음 | -클러스터의 Hadoop 계열에 맞게 `agents/hdfs//`에서 `grantforge-agent-hdfs--.jar`를 선택하세요. 공통 로직은 Java 8, 3.5 어댑터는 Java 17을 대상으로 합니다. +예를 들어 Hadoop 2.10.2에는 `agents/hdfs/2.10/grantforge-agent-hdfs-2.10-.jar`를 설치합니다. NameNode마다 한 버전의 에이전트만 설치하고, 업그레이드할 때 이전 jar를 제거하며 구성의 provider 클래스 이름은 그대로 둡니다. 에이전트는 시작할 때 Hadoop 주/부 버전과 필수 콜백을 확인하고 버전이 맞지 않으면 시작을 멈춥니다. 같은 주/부 버전의 다른 패치나 벤더 배포판은 따로 검증해야 합니다. -Hadoop 2.7, 2.10, 3.2, 3.3에는 에이전트가 사용하는 슈퍼유저 인가 콜백이 없습니다. 이러한 슈퍼유저 접근은 Hadoop이 관리합니다. GrantForge 정책으로 제어할 데이터 접근에는 일반 사용자를 사용하세요. +`common`과 `2.7`~`3.4`의 에이전트 본체는 Java 8 바이트코드를, `3.5`는 Java 17을 사용합니다. 위 표는 통합 테스트에서 실제로 사용한 JVM이며, 3.4.3을 Java 8로 검증했다는 뜻은 아닙니다. 3.3.6 공식 테스트 이미지는 amd64만 제공하므로 ARM 호스트에서는 명시적으로 amd64 에뮬레이션으로 실행합니다. ## 권한 관계 -다음 집행 동작은 일반 사용자의 접근을 설명합니다. 슈퍼유저 제한은 위 내용과 해당 콜백 설명을 참고하세요. - -에이전트는 HDFS 기본 권한 검사를 먼저 수행한 뒤 GrantForge 정책을 수행합니다. 사용자는 기본 권한과 정책을 모두 만족해야 합니다. GrantForge의 허용 정책은 POSIX 권한, ACL, 소유자 검사, sticky bit을 우회하지 않으며, 거부 정책은 항상 거부합니다. 기본 권한 설정은 여전히 Hadoop의 관리 도구로 관리합니다. +에이전트는 먼저 HDFS 네이티브 권한 검사를 수행한 뒤 GrantForge 정책을 적용합니다. 사용자는 네이티브 권한과 정책 요구를 모두 충족해야 합니다. GrantForge의 허용 정책은 POSIX 권한, ACL, 소유자 검사, sticky bit를 우회하지 않으며, 거부 정책은 항상 거부합니다. 네이티브 권한 설정은 계속 Hadoop 관리 도구로 관리합니다. -기본값인 `grantforge.hdfs.native.fallback=false`는 로컬 정책 스냅샷이 없거나, 일치하는 정책이 없거나, 에이전트가 아직 시작하지 않았을 때 데이터 접근을 거부합니다. `true`로 지정하면 정책이 결정하지 않은 접근은 기본 권한을 사용하고, 명시적인 거부 정책은 그대로 효력이 있습니다. 서버가 일시적으로 닿지 않는 동안에도 마지막으로 서명 검증을 통과한 로컬 스냅샷을 계속 사용합니다. +기본값은 `grantforge.hdfs.native.fallback=false`입니다. 로컬 정책 스냅숏이 없거나, 일치하는 정책이 없거나, 에이전트가 아직 시작되지 않았으면 데이터 접근을 거부합니다. `true`로 설정하면 정책이 결정하지 않은 접근에는 네이티브 권한을 사용하며, 명시적인 거부 정책은 여전히 유효합니다. 서버에 일시적으로 연결할 수 없으면 서명 검증을 통과한 마지막 로컬 스냅숏을 계속 사용합니다. -하나의 인가 콜백에 들어 있는 조상, 대상, 서브트리, 스냅샷 경로 투영은 모두 같은 버전의 정책 스냅샷을 사용합니다. 갱신된 정책은 다음 콜백부터 효력이 생기므로, 서로 다른 버전의 허용 규칙이 조합되는 일은 없습니다. 접근 감사에는 실제로 사용된 정책 버전이 기록됩니다. +한 번의 인가 콜백 안에서 조상, 대상, 하위 트리, 스냅숏 경로 투영은 같은 버전의 정책 스냅숏을 사용합니다. 갱신된 정책은 다음 콜백부터 적용되므로 서로 다른 버전의 허용 규칙이 섞이지 않습니다. 접근 감사에는 실제로 사용한 정책 버전이 기록됩니다. -에이전트는 일반 사용자가 대상에 접근하는 데 필요한 `read`, `write`, `execute`를 검사하고, 부모 디렉터리와 조상 디렉터리, 재귀 검증이 필요한 하위 디렉터리도 검사합니다. 생성, 삭제, 이름 변경 같은 작업은 여러 경로를 얽히므로 허용 정책이 모두를 커버해야 합니다. 엄격 모드에서는 대상 파일에 `read` 정책만 있는 것으로는 부족하고, 사용자에게 조상 디렉터리의 `execute` 정책이 필요합니다. 예를 들어 `/`에서 `execute`를 허용하고 재귀를 체크한 뒤, 실제 데이터 디렉터리에 읽기와 쓰기 권한을 부여하세요. +에이전트는 일반 사용자가 대상에 접근하는 데 필요한 `read`, `write`, `execute`를 검사하고, 상위 디렉터리, 조상 디렉터리, 재귀 검증이 필요한 하위 디렉터리도 검사합니다. 생성, 삭제, 이름 변경 같은 작업은 여러 경로에 걸치므로 허용 정책이 이를 모두 포함해야 합니다. 엄격 모드에서는 대상 파일의 `read` 정책만으로는 부족하며, 사용자에게 조상 디렉터리의 `execute` 정책도 구성해야 합니다. 예를 들어 `/`에 대한 `execute`를 재귀로 허용하고, 실제 데이터 디렉터리에 읽기와 쓰기 권한을 구성합니다. -스냅샷 경로는 실제 요청 경로와 `.snapshot/<스냅샷 이름>`을 뗀 원래 경로를 함께 검사합니다. 예를 들어 `/data/.snapshot/s1/secret`은 `/data/secret`로도 검사됩니다. 따라서 원래 경로의 거부 정책이 스냅샷에도 적용되고, 명시적인 스냅샷 경로에는 더 엄격한 제한을 따로 둘 수 있습니다. 메타데이터 조회는 HDFS의 디렉터리 순회 권한 의미를 그대로 따릅니다. +스냅숏 경로는 실제 요청 경로와 `.snapshot/<스냅숏 이름>`을 뺀 원래 경로를 모두 검사합니다. 예를 들어 `/data/.snapshot/s1/secret`은 `/data/secret`도 검사합니다. 따라서 원래 경로의 거부 정책은 스냅숏에도 적용되며, 명시적인 스냅숏 경로에 더 엄격한 제한을 추가할 수 있습니다. 메타데이터 조회는 HDFS 디렉터리 탐색 권한 의미를 따릅니다. -재귀 인가는 한 번에 최대 `100000`개의 inode를 검사하며, 한도를 넘으면 NameNode 안에서 메모리를 무한히 할당하지 않도록 작업을 거부합니다. 지나치게 긴 경로는 전체 경로로 정책을 판단하고, 감사의 리소스 표시는 `1000`자로 제한하며 요청 상세에 원래 길이와 SHA-256 요약을 기록합니다. +한 번의 재귀 인가는 최대 `100000`개의 inode를 검사하며, 상한을 넘으면 작업을 거부해 NameNode 안에서 메모리를 무한히 할당하지 않도록 합니다. 아주 긴 경로는 전체 경로로 정책을 판정합니다. 감사 리소스 표시는 `1000`자로 제한되며, 요청 세부 정보에 원래 길이와 SHA-256 다이제스트를 기록합니다. -다음 슈퍼유저 콜백 설명은 Hadoop 3.4와 3.5에 적용됩니다. HDFS 슈퍼유저는 여전히 Hadoop이 관리합니다. 경로가 있는 슈퍼유저 콜백은 먼저 Hadoop의 슈퍼유저 검사를 통과한 뒤, Hadoop 3.4 / 3.5이 제공하는 작업 이름으로 정책을 검사합니다. 파일 읽기와 메타데이터 조회는 `read`, 디렉터리 열거는 `read` + `execute`, 알려진 수정 작업은 `write`를 요구합니다. 알 수 없거나, 존재하지 않거나, 정확히 추론할 수 없는 작업(예: `checkAccess`, `concat`)은 보수적으로 세 가지 권한을 모두 요구합니다. +Hadoop 2.7, 2.10, 3.2, 3.3은 에이전트를 호출하기 전에 슈퍼유저를 건너뛰므로 에이전트가 이 접근을 통제하거나 감사할 수 없습니다. Hadoop 3.4, 3.5의 경로가 있는 슈퍼유저 콜백은 먼저 네이티브 검사를 통과한 뒤 작업 이름에 따라 정책을 검사합니다. 파일 읽기와 메타데이터 조회에는 `read`, 디렉터리 나열에는 `read` + `execute`, 알려진 수정 작업에는 `write`가 필요합니다. 알 수 없거나 누락되었거나 정확히 추론할 수 없는 작업(예: `checkAccess`, `concat`)에는 보수적으로 세 가지 권한을 모두 요구합니다. -슈퍼유저 콜백에는 전체 inode와 서브트리 컨텍스트가 없고, 경로가 없는 클러스터 관리 호출은 기본 검사를 유지합니다. 하위 디렉터리 정책으로 슈퍼유저의 모든 재귀 작업을 제한할 수는 없습니다. 데이터 소비자는 일반 Hadoop 사용자를 써야 합니다. +슈퍼유저 콜백에는 완전한 inode와 하위 트리 문맥이 없으므로, 경로가 없는 클러스터 관리 호출은 네이티브 검사를 유지합니다. 하위 디렉터리 정책으로 슈퍼유저의 모든 재귀 작업을 제한할 수는 없습니다. 데이터 사용자는 일반 Hadoop 사용자를 사용해야 합니다. ## 지표 -에이전트는 Hadoop의 Metrics2 체계를 통해 지표를 보고하며, NameNode 자체의 dfs 지표와 같은 sink를 사용합니다. NameNode의 JMX에서는 `Hadoop:service=NameNode,name=GrantForgeHdfsAgent`로 나타나며 Prometheus의 JMX 익스포터가 바로 수집할 수 있습니다. 모든 지표는 `instance`(데이터 서비스 인스턴스 이름)와 `agentVersion` 태그를 가지므로, HA의 두 NameNode를 구분해 볼 수 있습니다. 지표 등록이 실패해도 잃는 것은 지표뿐입니다. 에이전트는 경고를 기록하고 지표 없이 계속 인가를 수행합니다. +에이전트는 Hadoop의 Metrics2 체계로 지표를 보고하며 NameNode 자체의 dfs 지표와 같은 sink를 사용합니다. NameNode의 JMX에서는 `Hadoop:service=NameNode,name=GrantForgeHdfsAgent`로 나타납니다(Prometheus JMX 익스포터로 바로 수집할 수 있습니다). 각 지표에는 `instance`(데이터 서비스 인스턴스 이름)와 `agentVersion` 레이블이 붙어 HA의 두 NameNode를 따로 볼 수 있습니다. 지표 등록에 실패해도 잃는 것은 지표뿐이며, 에이전트는 경고를 기록하고 지표 없이 인가를 계속합니다. | 지표 | 설명 | | --- | --- | -| `Callbacks` | 에이전트가 수행한 인가 콜백 수 | -| `SuperuserCallbacks` | 에이전트가 수행한 슈퍼유저 콜백 수 (3.4 / 3.5) | -| `NativeDenies` | 에이전트보다 앞서 Hadoop이 거부한 접근 수 | -| `EvaluationFailures` | 정책 평가 예외로 실패 닫힌 콜백 수 | -| `DecisionsAllowed` / `DecisionsDenied` / `DecisionsUndetermined` | 정책이 허용 / 거부 / 미결로 판단한 권한 수. 미결은 엄격 모드에서 역시 거부 | -| `MissingSnapshots` | 검증된 정책 스냅샷이 없는 동안 처리한 콜백 수 | -| `SnapshotVersion` | 현재 사용하는 정책 스냅샷의 버전, 없으면 0 | -| `QueuedEvents` / `DroppedEvents` | 메모리에서 보고를 기다리는 감사 이벤트 수 / 큐나 디스크 버퍼가 차서 버려진 이벤트 수 | +| `Callbacks` | 에이전트가 실행한 인가 콜백 수 | +| `SuperuserCallbacks` | 에이전트가 실행한 슈퍼유저 콜백 수 | +| `NativeDenies` | 에이전트보다 먼저 Hadoop이 거부한 접근 수 | +| `EvaluationFailures` | 정책 평가 예외로 실패 시 닫힘 처리된 콜백 수 | +| `DecisionsAllowed` / `DecisionsDenied` / `DecisionsUndetermined` | 정책이 허용 / 거부 / 미결정으로 판정한 권한 수. 미결정도 엄격 모드에서는 거부됩니다 | +| `MissingSnapshots` | 검증된 정책 스냅숏 없이 처리한 콜백 수 | +| `SnapshotVersion` | 현재 사용 중인 정책 스냅숏 버전. 0은 없음을 뜻합니다 | +| `QueuedEvents` / `DroppedEvents` | 메모리에서 보고를 기다리는 감사 이벤트 수 / 큐나 디스크 버퍼가 가득 차서 버린 이벤트 수 | | `ServerReachable` | 마지막 정책 서버 접근이 성공했는지 여부(1/0) | ## 배포 -1. GrantForge의 데이터 서비스에 `hdfs` 서비스를 추가하고 설정을 저장한 뒤 연결을 테스트하며, 실제 Hadoop 짧은 사용자 이름, 사용자 그룹 또는 역할에 경로 정책을 설정합니다. -2. "데이터 권한 → 에이전트"에서 이 서비스의 토큰을 발급합니다. 토큰 원문을 각 NameNode의 로컬 파일에 쓰며, 예를 들어 `/etc/hadoop/grantforge/token`으로 두고 NameNode를 실행하는 사용자가 읽을 수 있게 합니다. -3. 클러스터의 Hadoop 계열에 맞게 `agents/hdfs//`의 `grantforge-agent-hdfs--.jar`를 NameNode 클래스패스에 복사하세요. 예: `$HADOOP_HOME/share/hadoop/hdfs/lib/`. 호환되는 어댑터 하나만 설치하세요. jar에는 공통 로직, Jackson과 서명 라이브러리가 포함되며 Hadoop 클래스는 NameNode가 제공합니다. -4. 각 NameNode의 `hdfs-site.xml`에 다음 속성을 설정합니다. HA의 두 NameNode는 서로 다른 `instance`와 각자의 로컬 캐시 디렉터리를 사용합니다. +1. GrantForge의 데이터 서비스에 `hdfs` 서비스를 추가하고 구성을 저장한 뒤 연결을 테스트합니다. 실제 Hadoop 짧은 사용자 이름, 사용자 그룹 또는 역할에 대해 경로 정책을 구성합니다. +2. "데이터 권한 → 에이전트"에서 이 서비스의 토큰을 발급합니다. 토큰 원문을 각 NameNode의 로컬 파일(예: `/etc/hadoop/grantforge/token`)에 쓰고 NameNode 실행 사용자가 읽을 수 있게 합니다. +3. 위 표에 따라 배포 패키지의 `agents/hdfs//`에서 해당 에이전트 jar를 골라 NameNode 클래스 경로(예: `$HADOOP_HOME/share/hadoop/hdfs/lib/`)에 둡니다. 에이전트 jar에는 자체 정책 엔진, Jackson, 서명 라이브러리가 들어 있고 Hadoop 클래스는 NameNode가 제공합니다. 하트비트의 에이전트 버전에는 제품 버전과 컴파일에 사용한 Hadoop 버전이 포함됩니다. +4. 각 NameNode의 `hdfs-site.xml`에 다음 속성을 구성합니다. HA의 두 NameNode는 서로 다른 `instance`와 각자의 로컬 캐시 디렉터리를 사용합니다. ```xml @@ -102,58 +100,56 @@ Hadoop 2.7, 2.10, 3.2, 3.3에는 에이전트가 사용하는 슈퍼유저 인 ``` -5. `dfs.permissions.enabled=true`인지, `dfs.namenode.inode.attributes.provider.bypass.users`가 비어 있는지 확인합니다. 인가 콜백을 우회할 수 있는 설정은 에이전트가 시작할 때 거부합니다. NameNode를 재시작한 뒤 GrantForge 에이전트 페이지에서 하트비트와 정책 버전을 확인합니다. 에이전트는 NameNode의 기존 설정을 읽기만 하며 inode의 기본 속성은 수정하지 않습니다. +5. `dfs.permissions.enabled=true`이고 `dfs.namenode.inode.attributes.provider.bypass.users`가 비어 있는지 확인합니다. 에이전트는 인가 콜백을 우회할 수 있는 구성에서는 시작을 거부합니다. NameNode를 다시 시작한 뒤 GrantForge 에이전트 화면에서 하트비트와 정책 버전을 확인합니다. 에이전트는 NameNode의 기존 구성을 읽기만 하며 inode의 네이티브 속성은 바꾸지 않습니다. -처음 배포할 때는 `native.fallback=true`로 시작해 정책 스냅샷이 동기화되고 조상 디렉터리 권한이 갖추어졌는지 확인한 뒤 엄격 모드로 전환할 수 있습니다. 토큰이 바인딩된 서비스 타입은 반드시 `hdfs`여 합니다. 설정이 잘못되거나 다른 서비스 타입에 바인딩되면 접근이 거부됩니다. +처음 배포할 때는 `native.fallback=true`로 시작해 정책 스냅숏이 동기화되었는지 확인하고 조상 디렉터리 권한을 보완한 뒤 엄격 모드로 전환할 수 있습니다. 토큰에 연결된 서비스 유형은 `hdfs`여야 하며, 구성이 잘못되었거나 다른 서비스 유형에 연결되면 접근이 거부됩니다. ## 선택 설정 | 속성 | 기본값 | 용도 | | --- | --- | --- | -| `grantforge.hdfs.connect.timeout.ms` | `5000` | GrantForge에 연결할 때의 타임아웃 | -| `grantforge.hdfs.read.timeout.ms` | `8000` | 응답을 읽을 때의 타임아웃 | -| `grantforge.hdfs.refresh.interval.ms` | `30000` | 서버에 닿지 않을 때의 정책 갱신 간격, 최소 `1000`. 정상 하트비트는 서버가 제안한 간격을 사용 | -| `grantforge.hdfs.signing.key.file` | 설정 안 됨 | 선택적인 서명 공개키 파일. 콘솔이 제공한 Base64 X.509 공개키 내용이 들어갑니다. 설정하면 그 공개키의 서명만 받아들입니다 | -| `grantforge.hdfs.audit.batch.size` | `500` | 한 번에 보고할 최대 이벤트 수, 범위 `1..1000` | -| `grantforge.hdfs.audit.queue.capacity` | `10000` | 메모리 감사 큐의 용량, 범위 `1..1000000`, 배치 하나는 반드시 담을 수 있어야 합니다. 큐가 차면 새 이벤트를 세고 버립니다 | -| `grantforge.hdfs.audit.flush.interval.ms` | `5000` | 감사 플러시 간격, 양의 정수, 최대 `2147483647`. 줄이면 보고 지연을 낮출 수 있습니다 | -| `grantforge.hdfs.audit.spool.limit.bytes` | `67108864` | 서버에 닿지 않을 때의 디스크 버퍼 상한, 음이 아닌 정수. `0`이면 디스크 버퍼를 끕니다 | - -접근량이 갑자기 많을 때는 감사 큐를 늘려 큐 넘침을 줄이고, 플러시 간격을 줄이면 감사 지연은 낮아지지만 보고 빈도는 늘어납니다. 디스크 버퍼 제한은 장시간 단절 동안의 디스크 사용량을 조절하며, 버퍼를 끄거나 다 쓰면 이벤트를 잃을 수 있습니다. 감사 보고는 백그라운드에서 수행되며 정책 서버의 응답을 기다리지 않습니다. +| `grantforge.hdfs.connect.timeout.ms` | `5000` | GrantForge 연결 시간 제한 | +| `grantforge.hdfs.read.timeout.ms` | `8000` | 응답 읽기 시간 제한 | +| `grantforge.hdfs.refresh.interval.ms` | `30000` | 서버에 연결할 수 없을 때의 정책 갱신 간격. 최소 `1000`이며, 정상 하트비트는 서버가 권장하는 간격을 사용합니다 | +| `grantforge.hdfs.signing.key.file` | 설정 안 함 | 선택적 서명 공개 키 파일. 내용은 콘솔이 제공하는 Base64 X.509 공개 키이며, 설정하면 이 키의 서명만 받아들입니다 | +| `grantforge.hdfs.audit.batch.size` | `500` | 한 번에 보고하는 최대 이벤트 수. 범위는 `1..1000` | +| `grantforge.hdfs.audit.queue.capacity` | `10000` | 메모리 감사 큐 용량. 범위는 `1..1000000`이며 최소 한 배치를 담을 수 있어야 합니다. 큐가 가득 차면 개수를 세고 새 이벤트를 버립니다 | +| `grantforge.hdfs.audit.flush.interval.ms` | `5000` | 감사 플러시 간격. 양의 정수이며 최대 `2147483647`. 줄이면 보고 지연이 줄어듭니다 | +| `grantforge.hdfs.audit.spool.limit.bytes` | `67108864` | 서버에 연결할 수 없을 때의 디스크 버퍼 상한. 음이 아닌 정수이며 `0`은 디스크 버퍼를 끕니다 | -서명 공개키를 설정하지 않으면 에이전트가 처음 서버에 접근할 때 공개키를 가져와 스냅샷과 함께 저장합니다. 에이전트는 Hadoop이 넘겨준 짧은 사용자 이름과 그룹을 사용하고, 역할과 추가 그룹은 서명된 스냅샷에서 옵니다. Kerberos principal의 짧은 이름 매핑은 클러스터의 `hadoop.security.auth_to_local`이 결정합니다. +접근이 갑자기 몰릴 때는 감사 큐를 키우면 큐 넘침이 줄어듭니다. 플러시 간격을 줄이면 감사 지연은 낮아지지만 보고 빈도는 늘어납니다. 디스크 버퍼 상한은 오랜 연결 끊김 동안의 디스크 사용량을 제한하기 위한 것이며, 버퍼를 끄거나 다 쓰면 이벤트가 유실될 수 있습니다. 감사 보고는 백그라운드에서 실행되며 정책 서버의 응답을 기다리지 않습니다. -## 소스 빌드와 검증 +서명 공개 키를 구성하지 않으면 에이전트는 처음에 서버에서 공개 키를 가져와 스냅숏과 함께 저장합니다. 에이전트는 Hadoop이 넘겨준 짧은 사용자 이름과 사용자 그룹을 사용하며, 역할과 추가 그룹은 서명된 스냅숏에서 가져옵니다. Kerberos principal의 짧은 이름 매핑은 클러스터의 `hadoop.security.auth_to_local`이 결정합니다. -다음 명령은 3.5 예시입니다. 대상 어댑터에 맞게 2.7, 2.10, 3.2, 3.3 또는 3.4로 바꾸세요. +## 소스에서 빌드 및 검증 ```sh ./mvnw -pl agents/grantforge-agent-hdfs-3.5 -am package ./mvnw -pl plugins/grantforge-plugin-hdfs,agents/grantforge-agent-hdfs-3.5,core/grantforge-plugin-host -am test ``` -에이전트 산출물은 `agents/grantforge-agent-hdfs-3.5/target/grantforge-agent-hdfs-3.5-<버전>.jar`에 생깁니다. 단위 테스트는 NameNode 인가 콜백, 설정, 버전 메타데이터, 정책 결정을 다루며 WebHDFS와 Kerberos 테스트는 로컬 임시 서비스를 띄웁니다. 실제 환경에 내놓기 전에 대상 클러스터에서도 읽기와 쓰기, 생성, 이름 변경, 재귀 삭제, HA 전환, 단절 뒤의 캐시 동작을 검증해야 합니다. +에이전트 산출물은 `agents/grantforge-agent-hdfs-3.5/target/grantforge-agent-hdfs-3.5-.jar`에 있습니다. 단위 테스트는 NameNode 인가 콜백, 구성, 버전 메타데이터, 정책 결정을 다루며, WebHDFS와 Kerberos 테스트는 로컬에 임시 서비스를 시작합니다. 운영에 투입하기 전에 대상 클러스터에서 읽기와 쓰기, 생성, 이름 변경, 재귀 삭제, HA 전환, 연결 끊김 뒤의 캐시 동작도 검증해야 합니다. -통합 검증은 `verify` 단계에서 Testcontainers로 실행합니다(단위 테스트는 클러스터를 띄우지 않으며, `verify`에는 사용 가능한 Docker daemon이 필요합니다). +통합 검증은 `verify` 단계에서 Testcontainers로 실행됩니다(단위 테스트는 클러스터를 시작하지 않으며, `verify`에는 사용 가능한 Docker 데몬이 필요합니다). ```sh ./mvnw -pl agents/grantforge-agent-hdfs-3.5 -am verify -# nightly와 같은 진입점을 사용합니다 -bash script/ci/hdfs_integration.sh 3.5 -# 한 계열 또는 전체 버전 행렬 -bash script/ci/hdfs_integration.sh all +# nightly와 같은 진입점 +bash script/ci/hdfs_integration.sh +# 지정한 버전 계열만 검증 +bash script/ci/hdfs_integration.sh 2.10 ``` -테스트는 버전이 고정된 Apache Hadoop 컨테이너 이미지를 사용하고, 실제로 패키징한 에이전트 jar를 NameNode 클래스패스에 넣습니다. Testcontainers는 격리된 네트워크를 만들고 NameNode와 DataNode의 수명 주기를 관리하며, 읽기와 쓰기, 생성, 추가, 이름 변경, 삭제, 재귀와 스냅샷 거부, 기본 권한과 감사, 정책 갱신, 정책 서버를 끊은 뒤 서명된 캐시로 NameNode를 재시작하는 경우, 스냅샷이 없을 때의 엄격 모드와 기본 권한 폴백을 검증합니다. +테스트는 버전을 고정한 Apache Hadoop 이미지를 사용합니다. 이전 버전 이미지는 다이제스트를 고정한 Java 8 기본 이미지와 SHA-512로 검증한 Apache 배포본으로 빌드합니다. 해당 버전의 실제 에이전트 jar를 NameNode 클래스 경로에 넣고 실제 Hadoop과 JVM 버전을 단언합니다. Testcontainers는 격리된 네트워크를 만들고 NameNode와 DataNode의 수명 주기를 관리하며, 읽기와 쓰기, 생성, 추가, 이름 변경, 삭제, 재귀 및 스냅숏 거부, 네이티브 권한과 감사, 정책 갱신, 정책 서버와 끊긴 상태에서 NameNode를 다시 시작해 서명된 캐시를 사용하는 것, 스냅숏이 없을 때의 엄격 모드와 네이티브 권한 대체를 검증합니다. -실행 중인 Docker daemon이 필요하고 테스트 이미지를 내려받을 수 있어야 합니다. Docker를 쓸 수 없으면 테스트는 실패하며 조용히 건너뛰지 않습니다. 파일 시스템 클라이언트는 Hadoop 컨테이너 안에서 실행되고, 정책 HTTP 서비스는 Testcontainers의 호스트 포트 포워딩을 사용하므로 외부 Hadoop 클러스터는 필요 없습니다. 테스트가 끝나면 컨테이너와 테스트 네트워크를 정리하고 로그는 `agents/grantforge-agent-hdfs-3.5/target/hdfs-testcontainers`에 저장합니다. +실행 중인 Docker 데몬과 테스트 이미지 다운로드 허용이 필요합니다. Docker를 사용할 수 없으면 테스트는 조용히 건너뛰지 않고 실패합니다. 파일 시스템 클라이언트는 Hadoop 컨테이너 안에서 실행되고 정책 HTTP 서비스는 Testcontainers의 호스트 포트 전달을 사용하므로 외부 Hadoop 클러스터가 필요 없습니다. 테스트가 끝나면 컨테이너와 테스트 네트워크를 정리하고, 로그는 `agents/grantforge-agent-hdfs-3.5/target/hdfs-testcontainers`에 저장합니다. -HA 테스트는 NameNode 두 개, DataNode 한 개, JournalNode 한 개를 띄우고 두 에이전트에 서로 다른 인스턴스 이름과 캐시 디렉터리를 설정합니다. 논리 HDFS 클라이언트로 활성 노드를 수동 전환하고 전환 뒤의 읽기와 쓰기, 거부 정책을 검증합니다. JournalNode 한 개는 테스트용일 뿐이므로 과반수 장애 허용을 검증하지 않으며 ZooKeeper 자동 장애 조치와도 무관합니다. +HA 테스트는 NameNode 두 개, DataNode 하나, JournalNode 하나를 시작하고 두 에이전트에 별도의 인스턴스 이름과 캐시 디렉터리를 구성합니다. 논리 HDFS 클라이언트로 활성 노드를 수동 전환하고 전환 후의 읽기, 쓰기, 거부 정책을 검증합니다. JournalNode 하나는 테스트 전용이며 과반수 내결함성은 검증하지 않고 ZooKeeper 자동 장애 조치도 다루지 않습니다. -자동 HA 테스트(Hadoop 2.10~3.5, Java 17과 21)는 ZooKeeper, JournalNode 3개, 각자 ZKFC를 가진 NameNode 2개, DataNode 1개를 시작합니다. 활성 NameNode를 강제로 중지하면 ZooKeeper가 다른 노드에 역할을 넘기고 그 노드가 정책을 계속 적용하며 거부가 자신의 인스턴스로 기록되는지, NameNode가 없는 동안 게시한 정책이 Standby로 복귀하면 적용되고 역할을 되돌린 뒤에도 이전 버전으로 돌아가지 않고 적용되는지, 정책 서버를 쓸 수 없을 때 넘겨받은 NameNode가 보유한 스냅샷으로 계속 적용하고 서버가 돌아오면 감사를 보내는지, JournalNode 3개 중 1개가 중지되면 쓰기가 성공하고 2개가 중지되면 쓰기가 실패하며 활성 NameNode가 쿼럼 없이 계속하지 않고 중지되는지 검증합니다. Kerberos 버전(Hadoop 3.5.0만)에서는 JournalNode도 keytab으로 로그인하고 HTTPS만 제공하며, ZKFC는 SASL로 ZooKeeper에 인증하고, 선출용 znode는 NameNode의 principal만 사용할 수 있습니다(인증되지 않은 ZooKeeper 클라이언트는 읽을 수도 없습니다). 활성 NameNode를 중지하면 다른 노드가 넘겨받아 Kerberos 사용자에게 정책을 계속 적용하는지, 티켓이 없는 클라이언트가 항상 거부되는지, 첫 NameNode가 돌아오면 역할을 되돌릴 수 있는지 검증합니다. JournalNode는 NameNode와 같은 짧은 이름의 principal로 실행합니다. JournalNode는 edits를 NameNode의 전체 principal이나 자신과 같은 짧은 이름의 요청자에게만 주는데, HTTP 인증은 짧은 이름만 전달하기 때문입니다. Hadoop 2.7의 자동 HA는 검증되지 않았습니다. 그 클라이언트는 모든 NameNode의 호스트 이름이 해석될 때만 장애 조치를 하는데, 중지된 컨테이너의 이름은 더 이상 해석되지 않으므로 다른 NameNode를 시도하지 않고 UnknownHostException으로 끝납니다. +자동 HA 테스트(Hadoop 2.10~3.5, Java 17과 21)는 ZooKeeper, JournalNode 세 개, 각각 ZKFC가 있는 NameNode 두 개, DataNode 하나를 시작합니다. 테스트는 다음을 검증합니다. 활성 NameNode를 직접 멈추면 ZooKeeper가 다른 노드를 골라 넘겨받고, 새 활성 노드가 계속 정책을 적용하며 거부 이벤트는 그 노드의 인스턴스에 귀속됩니다. 노드가 빠져 있는 동안 게시된 새 정책은 그 노드가 Standby로 복귀한 뒤 적용되고, 다시 전환해도 이전 버전으로 돌아가지 않고 새 버전을 적용합니다. 정책 서비스를 사용할 수 없으면 넘겨받은 노드가 이미 불러온 스냅숏으로 계속 적용하고, 서비스가 복구되면 감사를 다시 보냅니다. JournalNode 세 개 중 하나를 멈춰도 쓰기는 성공하고, 두 개를 멈추면 쓰기가 실패하고 활성 NameNode가 멈추며 과반수 없이 계속하지 않습니다. Kerberos 버전(Hadoop 3.5.0만)도 있습니다. JournalNode도 keytab으로 로그인하고 HTTPS만 열며, ZKFC는 SASL로 ZooKeeper에 인증하고, 선출 노드에는 NameNode의 principal만 접근할 수 있습니다(인증하지 않은 ZooKeeper 클라이언트는 읽기조차 거부됩니다). 테스트는 활성 NameNode를 멈추면 다른 노드가 넘겨받아 Kerberos 사용자에게 계속 정책을 적용하고, 티켓이 없는 클라이언트는 항상 거부되며, 원래 노드가 복귀하면 다시 전환할 수 있음을 검증합니다. JournalNode는 NameNode와 같은 짧은 이름의 principal로 실행합니다. JournalNode는 edits를 NameNode의 전체 principal이나 자신과 같은 짧은 이름의 요청자에게만 넘기는데, HTTP 인증은 짧은 이름만 제공하기 때문입니다. Hadoop 2.7의 자동 HA는 검증하지 않았습니다. 그 클라이언트는 모든 NameNode 호스트 이름을 확인할 수 있을 때만 전환하는데, 테스트에서 멈춘 컨테이너 이름은 더 이상 확인되지 않으므로 클라이언트가 다른 노드를 시도하지 않고 바로 UnknownHostException을 보고합니다. -Kerberos 테스트는 여섯 개 버전 라인 모두에서 실행합니다(Java 17과 21). KDC는 테스트 JVM 안에서 실행되고, NameNode와 DataNode는 각자의 keytab으로 보안 모드로 시작하며, DataNode는 SASL을 거친 데이터 전송과 HTTPS만 제공하고 WebHDFS는 SPNEGO를 사용합니다. principal이 짧은 이름으로 매핑된 뒤 GrantForge 정책이 읽기와 쓰기에 적용되는지, 정책이 없는 사용자가 거부되는지, 거부가 짧은 이름으로 감사되는지, 티켓이 없는 클라이언트가 simple 인증으로 돌아가지 않고 거부되는지, 재시작한 NameNode가 다시 로그인해 정책을 계속 적용하는지 검증합니다. +Kerberos 테스트는 여섯 개 버전 계열 모두에서 실행됩니다(Java 17과 21). KDC는 테스트 JVM 안에서 실행되고, NameNode와 DataNode는 각자 keytab을 사용해 보안 모드로 시작합니다. DataNode의 데이터 전송에는 SASL이 필요하고 HTTPS만 열며, WebHDFS는 SPNEGO를 사용합니다. 테스트는 principal을 짧은 이름으로 매핑한 뒤 GrantForge 정책이 읽기와 쓰기에 적용되는지, 정책이 없는 사용자가 거부되는지, 거부 이벤트가 짧은 이름으로 감사되는지, 티켓이 없는 클라이언트가 거부되고 simple 인증으로 대체되지 않는지, NameNode가 다시 시작된 뒤 다시 로그인해 정책을 계속 적용하는지를 검증합니다. -공통 네이티브 단위 테스트는 `agents/grantforge-agent-hdfs-native/src/test`에서 실행하며 여섯 어댑터에서 다시 컴파일하지 않습니다. 버전별 콜백 테스트는 각 번호별 모듈에 유지합니다. 공통 컨테이너 테스트 소스는 계속 `agents/grantforge-agent-hdfs-common/src/test/shared`에 두고 번호별 프로덕션 모듈에서 컴파일합니다. 별도의 Maven 테스트 프로젝트는 만들지 않고 Testcontainers는 test 의존성으로 유지합니다. nightly는 Java 17/21 테스트 호스트에서 여섯 Hadoop 버전을 검사하며 컨테이너 내부 Java는 위 표를 따릅니다. 보고서와 컨테이너 로그를 저장합니다. +테스트 소스는 실제 운영 모듈의 `src/test`에 있습니다. 공통 정책 단위 테스트는 common에서, 공통 네이티브 단위 테스트는 `agents/grantforge-agent-hdfs-native/src/test`에서 실행되며 여섯 개 버전 에이전트에는 컴파일되지 않습니다. 버전별 콜백 테스트는 해당 번호 모듈에 남아 있고, 공통 컨테이너 테스트 소스 `agents/grantforge-agent-hdfs-common/src/test/shared`는 여전히 각 번호 모듈에 컴파일됩니다. 별도의 Maven 테스트 프로젝트는 없습니다. Testcontainers는 test 범위에만 있습니다. nightly는 Java 17과 21 호스트에서 여섯 개 Hadoop 버전을 검증하며, Hadoop 컨테이너는 위 표의 JVM을 사용합니다. 현재 컨테이너는 Simple 인증, 수동 HA, 여섯 개 버전 계열 모두의 Kerberos와 HTTPS, 2.10~3.5의 자동 HA, Hadoop 3.5.0에서 Kerberos 기반 자동 HA를 다룹니다. 2.7의 자동 HA, 다른 버전의 Kerberos 기반 자동 HA, 벤더 패치는 대상 환경에서 검증해야 합니다. -Hadoop 확장 진입점과 권한 의미는 [Apache Hadoop 3.5.0 API](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/build/source/hadoop-hdfs-project/hadoop-hdfs/target/api/org/apache/hadoop/hdfs/server/namenode/INodeAttributeProvider.html)와 [HDFS 권한 설명서](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/HdfsPermissionsGuide.html)를 참고하세요. +Hadoop 확장 지점과 권한 의미는 [Apache Hadoop 3.5.0 API](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/build/source/hadoop-hdfs-project/hadoop-hdfs/target/api/org/apache/hadoop/hdfs/server/namenode/INodeAttributeProvider.html)와 [HDFS 권한 가이드](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/HdfsPermissionsGuide.html)를 참고하십시오. diff --git a/docs/content/ko/guide/catalog.md b/docs/content/ko/guide/catalog.md index 14909386..b1d1000e 100644 --- a/docs/content/ko/guide/catalog.md +++ b/docs/content/ko/guide/catalog.md @@ -22,6 +22,12 @@ description: 애플리케이션과 리소스 트리, 리소스 의존 관계, OA - **OAuth 클라이언트**: 비즈니스 애플리케이션용 클라이언트를 등록합니다. [OAuth 2.1과 OpenID Connect](/ko/integration/oauth/)를 참고하세요. - **필드**: 필드를 선택하면 그 필드가 어떤 인터페이스에 나타나는지(반환하거나 수신하는지)를 보여줍니다. +리소스가 있는 애플리케이션 카탈로그에서 **내보내기**를 누르면 개발이나 AI 도구에서 참고할 JSON 파일을 내려받을 수 있습니다. 파일에는 애플리케이션 정보, 계층으로 정리된 전체 리소스와 리소스 의존 관계가 들어 있고, 의존 관계 양쪽 리소스의 코드도 함께 담깁니다. 검색과 필터는 내보내는 내용에 영향을 주지 않습니다. `formatVersion`은 내보내기 형식의 버전을 나타냅니다. + +대상 애플리케이션을 선택하고 **가져오기**를 눌러 버전 1 내보내기 파일을 올린 뒤 먼저 사전 검사를 합니다. 가져오기는 같은 애플리케이션에서 유형과 상위가 일치하는 리소스만 리소스 코드로 재사용하며 기존 설정을 덮어쓰지 않습니다. 유형, 상위 또는 의존 유형이 충돌하면 가져오기 전체가 적용되지 않습니다. 새 리소스와 의존 관계는 확인 후 한 번에 기록되며, 같은 파일을 다시 가져와도 중복 항목이 생기지 않습니다. 기본 제공 애플리케이션은 가져오기를 지원하지 않으며, 선언형 의존 관계는 가져온 뒤 수동 의존 관계가 됩니다. 리소스를 가져오려면 리소스 생성 권한이, 의존 관계까지 가져오려면 리소스 수정 권한도 필요합니다. + +내보내기에는 GrantForge에 등록된 정보만 들어갑니다. 페이지 등 리소스의 `route`는 프런트엔드 경로이고 API 리소스의 `code`는 권한 코드입니다. 외부 업무 API의 HTTP 메서드와 요청 경로는 등록되어 있지 않으면 자동으로 추론하지 않습니다. JSON은 내보낸 시점의 스냅숏이며 어떤 사용자의 권한 부여 결과도 담지 않으므로, 리소스가 바뀌면 다시 내보내야 합니다. + ## API 카탈로그 **플랫폼 관리 → API 카탈로그**는 서버가 시작할 때 자동 등록된 모든 인터페이스와 각각의 접근 요구 사항을 나열합니다. 공개, 로그인만 하면 사용 가능, 또는 특정 권한 코드 필요 중 하나입니다. 권한이 필요한 인터페이스는 권한 코드별로 리소스 카탈로그에 분류되며, 역할에 권한을 부여할 때 이 권한을 참조합니다. 인터페이스가 추가되거나 폐기되거나 권한 코드가 바뀌면 "확인 대기 중인 변경"으로 표시되며, 확인하면 카탈로그가 업데이트됩니다. diff --git a/docs/content/ko/guide/explain.md b/docs/content/ko/guide/explain.md index a0379216..c0ae2857 100644 --- a/docs/content/ko/guide/explain.md +++ b/docs/content/ko/guide/explain.md @@ -13,6 +13,8 @@ description: 한 사람이 무엇을 할 수 있고 그 이유는 무엇인지 **사용자 관리**의 사용자 행에서 "유효 권한 보기"를 클릭하면 해당 사용자가 지금 사용할 수 있는 모든 것을 볼 수 있습니다. 역할, 메뉴와 버튼, API, 데이터 범위, 제한된 필드입니다. +애플리케이션을 선택하면 그 애플리케이션의 유효 리소스와 API 권한을 볼 수 있으며, "설명"은 역할 부여와 사용자 직접 부여의 출처를 보여 줍니다. 역할, 데이터 범위, 필드 권한은 사용자 전체 구성을 표시합니다. 역할 추가나 제거를 시뮬레이션해도 기존 직접 부여는 유지됩니다. + ![유효 권한](/screenshots/user-permissions.png) 항목의 **설명**을 클릭하면 GrantForge가 "왜 사용할 수 있는지"를 답합니다. 어떤 역할에서, 어떤 배정(직접, 사용자 그룹, 부서, 직위)과 어떤 리소스 도출을 통해 얻었는지입니다. 사용할 수 없을 때는 권한 부여를 받지 않은 것인지, 아니면 어떤 거부 규칙에 막힌 것인지를 알려줍니다. diff --git a/docs/content/ko/guide/identity-sources.md b/docs/content/ko/guide/identity-sources.md index fc988404..d2e20bf4 100644 --- a/docs/content/ko/guide/identity-sources.md +++ b/docs/content/ko/guide/identity-sources.md @@ -44,6 +44,11 @@ description: 회사 디렉터리(LDAP/AD)나 OpenID Connect 제공자로 사용 | 클라이언트 ID / 시크릿 | 제공자에 등록한 클라이언트입니다. 시크릿이 없으면 공개 클라이언트로 동작하며 PKCE를 사용합니다 | | 콜백 주소 | 페이지에 표시되는 `/api/v1/auth/federated/callback/<인코딩>` 주소이며, 제공자의 클라이언트에 등록해야 합니다 | | 범위와 클레임 | 기본값은 `openid profile email`입니다. 사용자 이름, 표시 이름, 이메일은 각각 `preferred_username`, `name`, `email` 클레임에서 가져옵니다 | +| 휴대폰 번호 클레임 | 기본값은 `phone`(Casdoor)이며 다른 제공자는 `phone_number`로 설정할 수 있습니다. 휴대폰 번호를 동기화하려면 scope에 `phone`이 포함되어야 합니다. 로그인할 때 제공자가 번호를 반환하지 않으면 기존 번호를 유지합니다 | +| 아바타 주소 | OIDC 표준 `picture` 클레임에서 동기화하며 scope에 `profile`이 포함되어야 합니다. 반환되지 않으면 기존 아바타 주소를 유지합니다 | +| 인가 요청 추가 매개변수 | 선택 사항인 JSON 객체이며 값은 모두 문자열이어야 합니다. 예를 들어 Casdoor는 `{"provider_hint":"github"}`로 구성된 제공자를 지정할 수 있습니다. 매개변수는 브라우저 주소에 표시되므로 비밀 값을 넣지 마십시오 | + +프로토콜 핵심 매개변수(`state`, `nonce`, `redirect_uri`, PKCE 매개변수 포함)는 GrantForge가 관리하며 추가 매개변수로 덮어쓸 수 없습니다. 활성화하면 로그인 페이지에 "<이름>으로 로그인" 버튼이 나타납니다. 사용자는 제공자에서 로그인한 뒤 GrantForge로 돌아오며, 2단계 인증을 켠 계정은 인증 코드도 입력해야 합니다. @@ -54,3 +59,26 @@ description: 회사 디렉터리(LDAP/AD)나 OpenID Connect 제공자로 사용 - 계정을 자동 생성하지 않는 신원 소스는 이미 연결된 사용자만 로그인하게 합니다. - 신원 소스를 비활성화하면 그 사용자는 로그인할 수 없으며, 계정이 사용 중인 신원 소스는 삭제할 수 없습니다. - 신원 소스 계정에도 평소와 같이 역할을 부여하고 2단계 인증을 켤 수 있습니다. + +조직 구조 동기화는 별도의 데이터 소스에서 구성하며 OIDC 로그인 설정을 바꿀 필요가 없습니다. + +## 데이터 소스와 조직 구조 동기화 + +데이터 소스는 조직 구조 동기화를 맡고, 신원 소스는 계속해서 OIDC 또는 LDAP 로그인을 맡습니다. 둘은 따로 구성하며 데이터 소스를 비활성화해도 OIDC 로그인에는 영향이 없습니다. + +1. "액세스 제어 → 신원 소스"에서 기존 OpenID Connect 로그인을 구성합니다. +2. "액세스 제어 → 데이터 소스"에서 DingTalk 데이터 소스를 추가하고 연결할 OIDC 신원 소스를 선택합니다. +3. CorpId, AppKey, AppSecret과 시작 부서 ID(보통 `1`)를 입력합니다. AppSecret은 따로 암호화해 저장하며 API는 평문을 반환하지 않습니다. 편집할 때 비워 두면 기존 값을 유지합니다. +4. 로컬 부서 아래에 마운트할 수 있습니다. 동기화 간격은 기본 30분이며 15–10080분을 지정할 수 있고, 비우면 수동으로만 동기화합니다. +5. 저장한 뒤 연결을 테스트하고 첫 동기화를 실행합니다. DingTalk 애플리케이션에는 부서와 직원 주소록을 읽을 권한이 있어야 하며, 그 권한 범위가 선택한 하위 트리를 포함해야 합니다. +6. 사용자가 SSO로 로그인하면 신뢰할 수 있는 OIDC 이메일로 직원을 찾아 부서와 주 부서를 자동으로 채웁니다. 이메일은 관리자가 관리하고 직원이 다른 사람을 사칭해 쓸 수 없어야 하며 DingTalk 이메일과 같아야 합니다. 직원 이메일이 없으면 기업 이메일을 사용합니다. + +로그인은 로컬 주소록 스냅숏만 읽고 DingTalk에 요청하지 않습니다. MFA를 사용하면 두 번째 단계 인증에 성공한 뒤 소속을 갱신합니다. 첫 동기화 전에 로그인한 계정은 신뢰할 수 있는 이메일을 임시로 보관했다가 동기화가 끝나면 연결합니다. 로컬에서 편집할 수 있는 프로필 이메일은 대조에 쓰지 않습니다. 없거나 중복되었거나 이미 다른 계정에 연결된 이메일로는 새 연결을 만들지 않습니다. + +직원 연결은 외부 직원 ID로 고정되어 이메일이 바뀌어도 다른 직원에게 옮겨 가지 않습니다. 동기화는 여러 부서 소속도 다룹니다. 주 부서는 여전히 유효한 기존 주 부서를 우선 유지하고, 그렇지 않으면 안정적인 부서 ID 순서로 고릅니다. 로컬 부서, 사용자 그룹, 직책, 직접 역할 부여는 유지됩니다. 동기화된 부서에 역할을 할당하면 사용자는 기존 부여 규칙에 따라 역할을 상속하고, 부서를 옮기면 권한도 갱신됩니다. + +조직 스냅숏이나 연결 대기 계정이 생긴 뒤에는 연결된 신원 소스, 기업, 애플리케이션, 시작 부서와 마운트 위치를 바꿀 수 없습니다. 동기화된 부서와 소속은 읽기 전용이며 사용자 관리나 조직 관리에서 직접 덮어쓸 수 없습니다. 사라진 부서는 기존 권한 부여를 지키기 위해 남겨 둡니다. 직원이 권한 범위의 주소록에서 빠지면 이 데이터 소스가 관리하는 소속을 제거하지만 계정을 자동으로 비활성화하지는 않습니다. 동기화된 부서를 가진 데이터 소스는 바로 삭제할 수 없고 대신 비활성화할 수 있습니다. + +동기화는 모든 페이지를 가져온 뒤 원자적으로 적용합니다. 읽기 실패, 계층 이상, 권한 범위 변화가 있으면 마지막으로 성공한 스냅숏을 유지하며, 관리자는 동기화 요약, 이상 목록, 감사 기록으로 결과를 확인할 수 있습니다. 권한 범위가 바뀌었다면 원래 승인된 범위로 되돌린 뒤 동기화하십시오. + +현재 [DingTalk 공식 서버 SDK](https://open.dingtalk.com/document/development/download-the-server-side-sdk)를 사용하며 HTTP 호출을 직접 구현하지 않습니다. 공급자 어댑터는 공통된 부서와 직원 스냅숏을 출력하므로, 나중에 Feishu 어댑터를 추가해도 같은 동기화, 연결, 권한 갱신 로직을 재사용할 수 있습니다. diff --git a/docs/content/ko/guide/users.md b/docs/content/ko/guide/users.md index b1464e5e..745a1210 100644 --- a/docs/content/ko/guide/users.md +++ b/docs/content/ko/guide/users.md @@ -31,6 +31,7 @@ description: 계정을 생성하고, 찾고, 편집하고, 비활성화하고, | 2단계 인증 재설정 | 인증기를 분실한 경우에 사용. 해당 계정의 2단계 인증을 끄고 세션을 종료함 | | 역할 보기 | 계정이 가진 역할과 각 역할의 출처(직접 배정, 사용자 그룹, 부서, 직위) | | 유효 권한 보기 | [권한 설명, 시뮬레이션과 감사](/ko/guide/explain/) 참고 | +| 직접 권한 부여 | 업무 애플리케이션을 선택해 사용자에게 메뉴, 페이지, 버튼 또는 API를 개별로 허용합니다. 만료 시간을 지정할 수 있습니다 | | 삭제 | 계정과 부서 관계를 삭제하며, 감사 기록은 보존됨 | 시스템 계정(초기화 시 생성된 관리자)과 당신 자신의 계정은 비활성화, 잠금, 삭제할 수 없습니다. @@ -38,3 +39,5 @@ description: 계정을 생성하고, 찾고, 편집하고, 비활성화하고, ## 자가 등록 기본적으로 꺼져 있습니다. `grantforge.security.registration-enabled=true`로 설정하면 로그인 페이지에 "계정 생성" 입구가 나타나고, 등록된 계정은 `grantforge.security.registration-tenant`이 지정한 테넌트로 들어가며 어떤 역할도 없는 일반 계정이 됩니다. + +직접 권한 부여는 역할 권한과 함께 계산되며 역할의 거부가 여전히 우선하고, 리소스 의존 관계는 기존 규칙대로 도출됩니다. 직접 권한 부여는 역할 기반 직무 분리 제약을 받지 않으며 데이터 행이나 필드 정책을 부여하지 않고 콘솔 애플리케이션에는 사용할 수 없습니다. 역할 부여 권한이 있고 대상 사용자를 수정할 수 있는 관리자만 사용할 수 있습니다. diff --git a/docs/content/ko/integration/javascript.md b/docs/content/ko/integration/javascript.md index 07bb0742..80ce7020 100644 --- a/docs/content/ko/integration/javascript.md +++ b/docs/content/ko/integration/javascript.md @@ -48,7 +48,8 @@ app.use(createGrantForge(grantForge)) ```vue - ``` + +``` `useGrantForge()`는 `authorization`, `error`, `refresh()`, `can()`, `hasResource()`를 제공합니다. 버튼을 숨기는 것은 편의일 뿐이며, 애플리케이션의 API도 같은 권한을 반드시 검증해야 합니다. diff --git a/docs/content/ko/start/quick-start.md b/docs/content/ko/start/quick-start.md index b1d146fb..06541810 100644 --- a/docs/content/ko/start/quick-start.md +++ b/docs/content/ko/start/quick-start.md @@ -38,7 +38,7 @@ WARN ... First-run setup is pending. Open the console and enter this setup toke ## 2. 초기화 완료 -브라우저에서 http://127.0.0.1:9999/을 열면 콘솔이 자동으로 초기화 페이지로 이동합니다. 로그에 출력된 토큰, 조직 이름, 첫 관리자의 사용자 이름과 비밀번호(12자 이상)를 입력합니다. +브라우저에서 을 열면 콘솔이 자동으로 초기화 페이지로 이동합니다. 로그에 출력된 토큰, 조직 이름, 첫 관리자의 사용자 이름과 비밀번호(12자 이상)를 입력합니다. ![초기 설정과 로그인 페이지](/screenshots/login.png) diff --git a/docs/content/pt-br/changelog/2026.4.0.md b/docs/content/pt-br/changelog/2026.4.0.md new file mode 100644 index 00000000..16d3ef8c --- /dev/null +++ b/docs/content/pt-br/changelog/2026.4.0.md @@ -0,0 +1,68 @@ +--- +title: "2026.4.0" +description: "GrantForge 2026.4.0 (2026-11-08): plug-ins de serviço e agentes para Elasticsearch, Apache Doris, MongoDB e Redis/Valkey, sincronização da organização a partir do DingTalk, permissões diretas de usuário, exportação e importação do catálogo de recursos e uma CI que confere se cada idioma está traduzido por completo." +--- + + +O 2026.4.0 estende as permissões de sistemas externos ao Elasticsearch, ao Apache Doris, ao MongoDB e ao Redis e Valkey. Os quatro agentes rodam ao lado do sistema e mantêm o controle de acesso dele de acordo com as políticas. Em identidade e autorização, esta versão traz a sincronização da organização a partir do DingTalk, permissões diretas de usuário e a exportação e importação do catálogo de recursos. Todas as mudanças estão na [comparação do GitHub](https://github.com/devlive-community/grantforge/compare/v2026.3.0...v2026.4.0). + +## Elasticsearch + +- Plug-in de serviço do Elasticsearch: índices e fluxos de dados, seus campos e o cluster; login com senha, chave de API ou certificado de cliente TLS; veja [Elasticsearch](/pt-br/plugins/elasticsearch/). +- Agente ao lado do cluster: mantém para cada usuário um papel próprio do Elasticsearch, atribuído aos usuários nativos e, por mapeamentos de papéis, aos usuários de outros realms, com segurança em nível de campo e de documento quando a licença permite; padrões de índice continuam sendo padrões, então índices novos ficam acessíveis na hora; veja [Agente do Elasticsearch](/pt-br/external/elasticsearch-agent/). +- Os acessos vêm dos arquivos do log de auditoria. Os testes de integração cobrem o Elasticsearch 8.19, 9.3, 9.4 e 9.5. + +## Apache Doris + +- Plug-in de serviço do Doris: catálogos, bancos de dados, tabelas, colunas e grupos de carga de trabalho; conecta-se a um ou mais FEs e faz login com senha; veja [Apache Doris](/pt-br/plugins/doris/). +- Agente ao lado do cluster: as políticas viram papéis próprios do Doris, permissões de coluna, permissões em grupos de carga de trabalho e políticas de linha, concedidos a cada identidade do usuário; um banco de dados ou um catálogo inteiro só é concedido quando o Doris não daria mais com isso; veja [Agente do Apache Doris](/pt-br/external/doris-agent/). +- Os acessos vêm da tabela do log de auditoria do Doris. Os testes de integração cobrem o Doris 2.1, 3.1, 4.0 e 4.1. + +## MongoDB + +- Plug-in de serviço do MongoDB: bancos de dados e coleções; conecta-se a um replica set ou a um cluster fragmentado e faz login com SCRAM, certificado de cliente, LDAP ou Kerberos; veja [MongoDB](/pt-br/plugins/mongodb/). +- Agente ao lado do deployment: mantém um papel do MongoDB por usuário e substitui seus privilégios em um só passo; com os privilégios herdados que o MongoDB informa, mostra o que papéis integrados e personalizados fora do GrantForge concedem de fato; veja [Agente do MongoDB](/pt-br/external/mongodb-agent/). +- Os acessos vêm dos arquivos do log de auditoria do Enterprise ou do Percona, ou do profiler. Os testes de integração cobrem o MongoDB 6.0, 7.0, 8.0 e 9.0, com Kerberos verificado no Percona Server for MongoDB. + +## Redis e Valkey + +- Plug-in de serviço do Redis: chaves, canais e nós; conecta-se a um servidor isolado com suas réplicas ou a um cluster e faz login como usuário de ACL, opcionalmente via TLS; veja [Redis](/pt-br/plugins/redis/). +- Agente ao lado do deployment: em cada nó, mantém os seletores de ACL de cada usuário de acordo com as políticas e os substitui com um único comando; as permissões raiz continuam com os operadores, e o agente pergunta ao Redis com `ACL DRYRUN` se elas permitem acessos que as políticas negam; veja [Agente do Redis](/pt-br/external/redis-agent/). +- As negações vêm do log de ACL de cada nó. Os testes de integração cobrem o Redis 7.2, 7.4, 8.2 e 8.10, o Valkey 8.1 e 9.1 e também um cluster Redis 8.10. + +## Agentes + +- Os agentes do ClickHouse, do PostgreSQL e do OpenSearch e os novos agentes desta versão que rodam ao lado de um sistema compartilham o novo núcleo `grantforge-agent-sync`: inicialização, configurações, temporizadores, ciclo de sincronização, posições de leitura da auditoria e acompanhamento de arquivos de log. Cada agente guarda só o que é próprio do seu sistema; os formatos de configuração e de log não mudam. + +## Identidade e organização + +- Fontes de dados: a sincronização da organização é configurada à parte das fontes de identidade, e o primeiro adaptador é o DingTalk, com o SDK oficial de servidor dele; depois de um login por SSO, os usuários são vinculados automaticamente aos seus departamentos e ao departamento principal pelo e-mail confiável; as tabelas migram nos seis bancos de dados suportados, inclusive SQL Server e Oracle; veja [Fontes de identidade (LDAP e OIDC)](/pt-br/guide/identity-sources/). +- Fontes de identidade OpenID Connect podem sincronizar o telefone e o avatar e acrescentar parâmetros à requisição de autorização (como `provider_hint` do Casdoor); os parâmetros do próprio protocolo não podem ser substituídos. + +## Autorização e catálogo de recursos + +- Permissões diretas de usuário: permitem a um único usuário menus, páginas, botões ou APIs de um aplicativo de negócio, opcionalmente até uma data de expiração; são calculadas junto com as permissões dos papéis, e uma negação vinda de um papel continua prevalecendo; veja [Usuários](/pt-br/guide/users/). +- O catálogo de recursos pode exportar a árvore de recursos de um aplicativo como JSON e importá-la em outro aplicativo: a importação passa primeiro por uma verificação e, havendo conflito, nada é aplicado; administradores da plataforma podem esvaziar as árvores de aplicativo elegíveis; veja [Catálogo de recursos e de API](/pt-br/guide/catalog/). + +## Servidor + +- Na inicialização, o servidor mostra um banner do GrantForge com sua versão e a do Java no lugar do banner do Spring Boot. + +## Documentação e publicação + +- A aba de plug-ins abre com uma visão geral, agrupa os sistemas por tipo em uma barra lateral recolhível e mostra para cada sistema como aplica as políticas, se suporta filtros de linha e mascaramento e quais versões foram verificadas; todos os READMEs listam os componentes na mesma ordem. +- A CI confere se cada página está traduzida por completo em cada idioma: os mesmos parágrafos, títulos, tabelas, blocos de código e links da página em chinês, nenhum texto na escrita de outro idioma e nada copiado da página em inglês; os 11 READMEs passam pelas mesmas verificações em relação ao README em inglês. Todas as traduções faltantes encontradas pela verificação foram completadas. +- A verificação de versões mantém atualizado o selo de versão de cada README. +- Os workflows de publicação da documentação e das versões não rodam mais em forks. +- O servidor `grantforge-server` não é mais publicado em repositórios Maven: ele inclui todos os plug-ins e passa do limite de tamanho deles; é distribuído no pacote da versão e na imagem. + +## Correções + +- Mudar o tipo de um recurso no editor de recursos limpa o código de recurso que deixou de servir. +- O último bloco de código do guia de integração com JavaScript não engole mais o texto que vem depois, e o endereço do console nas páginas do Docker e do início rápido não transforma mais o texto seguinte em link. +- Dependências atualizadas para versões que corrigem alertas de segurança: MariaDB Connector/J 3.5.11, OpenTelemetry 1.67.0, Guava 33.7.2, Snappy 1.1.10.9, além de vue-i18n, glob e minimatch no console e no SDK. diff --git a/docs/content/pt-br/deploy/docker.md b/docs/content/pt-br/deploy/docker.md index 92128347..e8bdf009 100644 --- a/docs/content/pt-br/deploy/docker.md +++ b/docs/content/pt-br/deploy/docker.md @@ -43,7 +43,7 @@ docker compose -f deploy/compose/postgres.yml up -d docker compose -f deploy/compose/postgres.yml logs grantforge | grep "setup token" ``` -Depois, abra http://127.0.0.1:9999/. A senha padrão do banco de dados usada pelos exemplos serve apenas para experimentar; altere-a com `GRANTFORGE_DB_PASSWORD` antes de entrar em produção. O exemplo de MySQL precisa que antes você coloque `mysql-connector-j-.jar` em `deploy/compose/drivers/`. +Depois, abra . A senha padrão do banco de dados usada pelos exemplos serve apenas para experimentar; altere-a com `GRANTFORGE_DB_PASSWORD` antes de entrar em produção. O exemplo de MySQL precisa que antes você coloque `mysql-connector-j-.jar` em `deploy/compose/drivers/`. ## Helm diff --git a/docs/content/pt-br/develop/development.md b/docs/content/pt-br/develop/development.md index 1c86c301..dcb34263 100644 --- a/docs/content/pt-br/develop/development.md +++ b/docs/content/pt-br/develop/development.md @@ -76,7 +76,7 @@ O script exige que a área de trabalho esteja limpa, que a branch local não est - `script/ci/release.sh` compila o pacote de lançamento, uma SBOM CycloneDX apenas com as dependências de publicação e `SHA256SUMS`; - imagens multiarquitetura são enviadas para `ghcr.io/devlive-community/grantforge`; -- os artefatos Maven (incluindo o pacote de fontes e o Javadoc) são publicados no GitHub Packages; quando o repositório tem `CENTRAL_USERNAME`, `CENTRAL_PASSWORD` (token do Central Portal), `GPG_PRIVATE_KEY` e `GPG_PASSPHRASE` configurados, eles são assinados e publicados no Maven Central; +- os artefatos Maven (incluindo o pacote de fontes e o Javadoc) são publicados no GitHub Packages; quando o repositório tem `CENTRAL_USERNAME`, `CENTRAL_PASSWORD` (token do Central Portal), `GPG_PRIVATE_KEY` e `GPG_PASSPHRASE` configurados, eles são assinados e publicados no Maven Central; o servidor `grantforge-server` inclui todos os plug-ins e passa do limite de tamanho dos repositórios, então não é publicado em nenhum repositório Maven e só é distribuído no pacote da versão e na imagem; - é criada uma GitHub Release cujo corpo reúne todos os commits desde a última versão publicada (uma tag `v*` ou numérica, como `1.0.6`), agrupados por novas funcionalidades, correções, desempenho etc., com links para os commits. Versões candidatas são marcadas como pré-lançamento e não atualizam o `latest` das imagens. Com o profile `central` ativado localmente, por padrão nada é publicado (`central.skip=true`); apenas o workflow de publicação passa explicitamente `-Dcentral.skip=false`. @@ -100,3 +100,5 @@ pnpm build # saída em docs/out ``` As páginas são Markdown em `docs/content/`, e a navegação fica em `docs/lib/navigation.ts`. A referência da API e os códigos de erro são gerados na compilação a partir do contrato e do código-fonte. As capturas de tela são geradas pelo `script/docs/screenshots.sh`, que inicia o serviço real, grava dados de exemplo e depois usa o Playwright. + +`pnpm test` também confere as traduções: cada página em chinês precisa de tradução para cada um dos outros dez idiomas, com os mesmos parágrafos, níveis de título, listas, tabelas, blocos de código e links da página em chinês. Uma página não pode conter texto em uma escrita que seu idioma não usa (incluindo caracteres simplificados no chinês tradicional e no japonês) nem frases copiadas sem alteração da página em inglês. Os 11 READMEs na raiz do repositório passam pelas mesmas verificações em relação ao README em inglês, e seus links para este site devem apontar para páginas existentes do próprio idioma. diff --git a/docs/content/pt-br/guide/catalog.md b/docs/content/pt-br/guide/catalog.md index 970d7773..39a03282 100644 --- a/docs/content/pt-br/guide/catalog.md +++ b/docs/content/pt-br/guide/catalog.md @@ -22,6 +22,12 @@ description: Mantenha a árvore de recursos e as dependências de cada aplicaç - **Clientes OAuth**: registre clientes para aplicações de negócio; ver [OAuth 2.1 e OpenID Connect](/pt-br/integration/oauth/). - **Campos**: ao selecionar um campo, são exibidas as interfaces em que ele aparece (que o retornam ou o recebem). +No catálogo de um aplicativo com recursos, clique em **Exportar** para baixar um arquivo JSON de referência para desenvolvimento ou ferramentas de IA. Ele contém as informações do aplicativo, todos os recursos organizados por hierarquia e suas dependências, com os códigos dos recursos nas duas pontas; a busca e os filtros não mudam o que é exportado. `formatVersion` indica a versão do formato de exportação. + +Escolha o aplicativo de destino, clique em **Importar**, envie um arquivo de exportação da versão 1 e faça primeiro a pré-verificação. A importação reutiliza recursos com o mesmo código no mesmo aplicativo apenas quando o tipo e o pai coincidem, sem sobrescrever configurações existentes; um conflito de tipo, pai ou tipo de dependência cancela a importação inteira. Recursos e dependências novos são gravados juntos após a confirmação, e repetir uma importação não cria duplicatas. Aplicativos integrados não aceitam importação; dependências declaradas viram dependências manuais após a importação. Importar recursos exige a permissão de criar recursos, e importar dependências exige também a de alterá-los. + +A exportação contém apenas o que está registrado no GrantForge: `route` de páginas e recursos semelhantes é a rota do frontend, e `code` dos recursos de API é o código de permissão. Métodos HTTP e caminhos de APIs de negócio externas não são deduzidos se não estiverem registrados. O JSON é um snapshot do momento da exportação e não inclui as concessões de nenhum usuário; exporte de novo quando os recursos mudarem. + ## Catálogo de API **Gestão da plataforma → Catálogo de API** lista todas as interfaces registradas automaticamente pelo servidor na inicialização e o que cada uma exige para acesso: ser pública, bastar o login ou exigir um código de permissão. As interfaces que exigem autorização são agrupadas no catálogo de recursos pelo seu código de permissão, e os papéis referenciam essas permissões ao conceder acesso. Quando uma interface é criada, desativada ou tem seu código de permissão alterado, ela aparece como "alteração pendente de confirmação" e o catálogo é atualizado após a confirmação. diff --git a/docs/content/pt-br/guide/explain.md b/docs/content/pt-br/guide/explain.md index fe7a088e..ee970c1c 100644 --- a/docs/content/pt-br/guide/explain.md +++ b/docs/content/pt-br/guide/explain.md @@ -13,6 +13,8 @@ description: Veja o que uma pessoa pode usar e por quê, simule mudanças de pap Em **Gestão de usuários**, clique em "Ver permissões vigentes" na linha de um usuário para ver tudo o que ele pode usar agora: papéis, menus e botões, API, escopo de dados e campos restritos. +Escolha um aplicativo para ver seus recursos e permissões de API vigentes; "Por quê" mostra se vêm de papéis ou de permissões diretas do usuário. Papéis, escopos de dados e permissões de campo mostram a configuração da conta inteira. Ao simular a inclusão ou remoção de papéis, as permissões diretas existentes são mantidas. + ![Permissões vigentes](/screenshots/user-permissions.png) Clique em **Explicação** em qualquer um desses itens e o GrantForge responde "por que pode usar": de qual papel, por meio de quais atribuições (direta, grupo de usuários, departamento, cargo) e de quais recursos isso é deduzido; quando não pode, ele explica se é falta de permissão ou se há alguma negação bloqueando. diff --git a/docs/content/pt-br/guide/identity-sources.md b/docs/content/pt-br/guide/identity-sources.md index de99ca71..366fe175 100644 --- a/docs/content/pt-br/guide/identity-sources.md +++ b/docs/content/pt-br/guide/identity-sources.md @@ -44,6 +44,11 @@ Escolha o tipo "OpenID Connect" para integrar o Keycloak, o Azure AD, o Okta, ou | ID / segredo do cliente | O cliente registrado no provedor; quando não há segredo, ele atua como cliente público, com PKCE | | Endereço de retorno | O `/api/v1/auth/federated/callback/` mostrado na página precisa ser registrado no cliente do provedor | | Escopos e claims | Por padrão `openid profile email`; nome de usuário, nome de exibição e e-mail são lidos das claims `preferred_username`, `name` e `email` | +| Claim de telefone | Lê `phone` por padrão (Casdoor); outros provedores podem usar `phone_number`. Para sincronizar o telefone, o escopo precisa incluir `phone`. Se o provedor não devolver um número no login, o número existente é mantido | +| Endereço do avatar | Sincronizado da claim padrão do OIDC `picture`; o escopo precisa incluir `profile`. Se nada for devolvido, o endereço existente é mantido | +| Parâmetros extras da requisição de autorização | Objeto JSON opcional cujos valores devem ser strings. No Casdoor, por exemplo, `{"provider_hint":"github"}` escolhe um provedor configurado. Os parâmetros aparecem no endereço do navegador, então nunca coloque segredos neles | + +O GrantForge gerencia os parâmetros essenciais do protocolo (incluindo `state`, `nonce`, `redirect_uri` e os do PKCE); parâmetros extras não podem substituí-los. Depois de habilitada, a página de login exibe o botão "Entrar com ". O usuário faz login no provedor e volta ao GrantForge; contas com a autenticação de dois fatores habilitada precisam informar também um código de verificação. @@ -54,3 +59,26 @@ Depois de habilitada, a página de login exibe o botão "Entrar com ". O u - Uma fonte de identidade que não cria contas automaticamente só permite o login de usuários já associados. - Depois de desabilitar uma fonte de identidade, seus usuários não conseguem fazer login; uma fonte de identidade que ainda tem contas em uso não pode ser excluída. - Às contas de fonte de identidade podem ser atribuídos papéis e habilitada a autenticação de dois fatores normalmente. + +A sincronização da estrutura organizacional é configurada em uma fonte de dados separada, sem alterar as configurações de login OIDC. + +## Fontes de dados e sincronização da organização + +As fontes de dados sincronizam a estrutura organizacional, e as fontes de identidade continuam responsáveis pelo login OIDC ou LDAP. As duas são configuradas separadamente: desabilitar uma fonte de dados não afeta o login OIDC. + +1. Configure o login OpenID Connect existente em "Controle de acesso → Fontes de identidade". +2. Em "Controle de acesso → Fontes de dados", adicione uma fonte do DingTalk e escolha a fonte de identidade OIDC associada. +3. Informe CorpId, AppKey, AppSecret e o ID do departamento raiz (normalmente `1`). O AppSecret é criptografado separadamente, a API nunca o devolve em texto claro e, se o campo ficar vazio na edição, o valor anterior é mantido. +4. A fonte pode ser montada sob um departamento local. O intervalo de sincronização padrão é de 30 minutos e aceita de 15 a 10080 minutos; vazio significa apenas sincronização manual. +5. Depois de salvar, teste a conexão e execute a primeira sincronização. O aplicativo do DingTalk precisa de permissão de leitura dos departamentos e do diretório de funcionários, e seu escopo autorizado deve cobrir a subárvore escolhida. +6. Quando um usuário faz login por SSO, o funcionário é localizado pelo e-mail OIDC confiável, e seus departamentos e o departamento principal são preenchidos automaticamente. O e-mail deve ser mantido por um administrador, não pode permitir que um funcionário se passe por outro e deve coincidir com o e-mail do DingTalk. Sem e-mail do funcionário, usa-se o e-mail corporativo. + +O login lê apenas o snapshot local do diretório e não consulta o DingTalk. Com MFA, a vinculação a departamentos é atualizada depois que a segunda etapa é concluída. Contas que fazem login antes da primeira sincronização guardam o e-mail confiável e são associadas quando a sincronização termina; o e-mail do perfil, editável localmente, nunca é usado na correspondência. E-mails ausentes, repetidos ou já vinculados a outra conta não criam novos vínculos. + +O vínculo com um funcionário se baseia no ID externo dele e não passa para outro funcionário quando o e-mail muda. A sincronização cobre o pertencimento a vários departamentos; o departamento principal continua sendo o anterior enquanto for válido e, caso contrário, é escolhido pela ordem estável dos IDs de departamento. Departamentos locais, grupos de usuários, cargos e atribuições diretas de papéis são preservados. Ao atribuir um papel a um departamento sincronizado, os usuários o herdam pelas regras existentes, e uma transferência atualiza as permissões. + +Assim que existirem um snapshot da organização ou contas aguardando associação, a fonte de identidade associada, a empresa, o aplicativo, o departamento raiz e o ponto de montagem ficam fixos. Departamentos e vínculos sincronizados são somente leitura e não podem ser sobrescritos manualmente na gestão de usuários ou da organização. Departamentos que desaparecem são mantidos para proteger as permissões existentes; quando um funcionário sai do diretório autorizado, os vínculos gerenciados por esta fonte são removidos, mas a conta não é desabilitada automaticamente. Uma fonte de dados que possui departamentos não pode ser excluída diretamente, mas pode ser desabilitada. + +A sincronização obtém todas as páginas antes de aplicar o resultado de forma atômica. Em caso de falha de leitura, hierarquia inválida ou mudança do escopo autorizado, o último snapshot bem-sucedido é mantido; os administradores conferem o resultado pelo resumo da sincronização, pela lista de anomalias e pelo registro de auditoria. Se o escopo mudou, restaure primeiro o escopo aprovado e só então sincronize de novo. + +O acesso usa o [SDK oficial de servidor do DingTalk](https://open.dingtalk.com/document/development/download-the-server-side-sdk), sem chamadas HTTP próprias. Os adaptadores de fornecedor produzem snapshots neutros de departamentos e funcionários, de modo que um futuro adaptador do Feishu poderá reutilizar a mesma lógica de sincronização, vinculação e atualização de permissões. diff --git a/docs/content/pt-br/guide/users.md b/docs/content/pt-br/guide/users.md index 3aa823f4..da16408e 100644 --- a/docs/content/pt-br/guide/users.md +++ b/docs/content/pt-br/guide/users.md @@ -31,6 +31,7 @@ Busque por nome de usuário, nome de exibição ou e-mail, filtre por estado (no | Redefinir autenticação de dois fatores | Para quando o autenticador é perdido: desativa a autenticação de dois fatores da conta e encerra suas sessões | | Ver papéis | Os papéis que a conta possui e a origem de cada um (atribuição direta, grupo de usuários, departamento, cargo) | | Ver permissões vigentes | Ver [Explicar, simular e auditar permissões](/pt-br/guide/explain/) | +| Conceder permissões diretas | Escolher um aplicativo de negócio e permitir somente a este usuário menus, páginas, botões ou APIs, com data de expiração opcional | | Excluir | Exclui a conta e suas relações com departamentos; os registros de auditoria são preservados | A conta do sistema (o administrador criado na inicialização) e a sua própria conta não podem ser desabilitadas, bloqueadas nem excluídas. @@ -38,3 +39,5 @@ A conta do sistema (o administrador criado na inicialização) e a sua própria ## Autorregistro Desativado por padrão. Depois de definir `grantforge.security.registration-enabled=true`, a página de login passa a exibir o acesso "Criar conta"; as contas registradas entram no tenant indicado em `grantforge.security.registration-tenant` e são contas comuns, sem nenhum papel. + +Permissões diretas são calculadas junto com as dos papéis; uma negação vinda de um papel continua prevalecendo, e as dependências de recursos são deduzidas como de costume. A segregação de funções baseada em papéis não se aplica a permissões diretas; elas não concedem políticas de linhas nem de campos e não podem ser usadas para o aplicativo do console. Somente administradores com permissão para atribuir papéis e alterar o usuário de destino podem gerenciá-las. diff --git a/docs/content/pt-br/integration/javascript.md b/docs/content/pt-br/integration/javascript.md index 6756dafb..0db2bd8e 100644 --- a/docs/content/pt-br/integration/javascript.md +++ b/docs/content/pt-br/integration/javascript.md @@ -48,7 +48,8 @@ app.use(createGrantForge(grantForge)) ```vue - ``` + +``` `useGrantForge()` fornece `authorization`, `error`, `refresh()`, `can()` e `hasResource()`. Ocultar botões é apenas uma conveniência: a API da aplicação precisa verificar as permissões da mesma forma. diff --git a/docs/content/pt-br/start/quick-start.md b/docs/content/pt-br/start/quick-start.md index a50cb99b..ee2a956f 100644 --- a/docs/content/pt-br/start/quick-start.md +++ b/docs/content/pt-br/start/quick-start.md @@ -38,7 +38,7 @@ Os registros do pacote de lançamento ficam em `logs/grantforge.log`; com o Dock ## 2. Concluir a inicialização -Abra http://127.0.0.1:9999/ no navegador e o console entrará automaticamente na página de inicialização. Informe o token que está no registro, o nome da organização e o usuário e a senha do primeiro administrador (com pelo menos 12 caracteres). +Abra no navegador e o console entrará automaticamente na página de inicialização. Informe o token que está no registro, o nome da organização e o usuário e a senha do primeiro administrador (com pelo menos 12 caracteres). ![Página de inicialização e de login](/screenshots/login.png) diff --git a/docs/content/ru/architecture/overview.md b/docs/content/ru/architecture/overview.md index 121f70cf..6636d60b 100644 --- a/docs/content/ru/architecture/overview.md +++ b/docs/content/ru/architecture/overview.md @@ -59,19 +59,19 @@ flowchart TB ```mermaid sequenceDiagram - participant B as 控制台 - participant F as 过滤链 + participant B as Консоль + participant F as Цепочка фильтров participant G as PermissionGuard - participant C as 控制器 - participant S as 领域服务 - B->>F: 会话 Cookie + X-XSRF-TOKEN - F->>F: 会话、CSRF、绑定租户、记录活动 - F->>G: 已登录 - G->>G: 读取账号的权限快照,检查接口声明的权限码 - G->>C: 通过(响应带 X-Authorization-Version) - C->>S: 调用服务,带上操作者 - S->>S: 数据范围、字段规则、业务校验 - S-->>B: JSON 或 RFC 9457 problem details + participant C as Контроллер + participant S as Доменный сервис + B->>F: Cookie сеанса + X-XSRF-TOKEN + F->>F: Сеанс, CSRF, привязка арендатора, запись активности + F->>G: Вход выполнен + G->>G: Прочитать снимок прав учётной записи, проверить код права, объявленный эндпоинтом + G->>C: Разрешено (ответ содержит X-Authorization-Version) + C->>S: Вызов сервиса с указанием исполнителя + S->>S: Области данных, правила полей, бизнес-проверки + S-->>B: JSON или RFC 9457 problem details ``` - Каждый метод контроллера обязан объявить свой режим доступа (публичный, любая вошедшая учётная запись или требуемый код права); метод без объявления не позволяет серверу запуститься. diff --git a/docs/content/ru/architecture/performance.md b/docs/content/ru/architecture/performance.md index 09dd9813..f69c1d25 100644 --- a/docs/content/ru/architecture/performance.md +++ b/docs/content/ru/architecture/performance.md @@ -42,9 +42,9 @@ description: Цели производительности при масштаб ## Запуск локально ```bash -bash script/ci/perf_benchmark.sh full # 目标规模,postgres:17(需要 Docker),检查上限 -bash script/ci/perf_benchmark.sh smoke # H2 上的小规模,只确认基准能运行 -bash script/ci/perf_benchmark.sh full mysql:8.4 # 其他数据库 +bash script/ci/perf_benchmark.sh full # целевой масштаб на postgres:17 (нужен Docker), проверка пределов +bash script/ci/perf_benchmark.sh smoke # малый масштаб на H2, только проверка, что тест запускается +bash script/ci/perf_benchmark.sh full mysql:8.4 # другая база данных ``` Отчёт записывается в `perf/target/perf-report.json`, а таблица дополнительно печатается в лог. Дополнительные опции можно передать через `PERF_OPTS`: diff --git a/docs/content/ru/changelog/2026.4.0.md b/docs/content/ru/changelog/2026.4.0.md new file mode 100644 index 00000000..49c7b3f9 --- /dev/null +++ b/docs/content/ru/changelog/2026.4.0.md @@ -0,0 +1,68 @@ +--- +title: "2026.4.0" +description: "GrantForge 2026.4.0 (2026-11-08): плагины сервисов и агенты для Elasticsearch, Apache Doris, MongoDB и Redis/Valkey, синхронизация оргструктуры из DingTalk, прямые права пользователей, экспорт и импорт каталога ресурсов и CI, проверяющая полноту перевода на каждый язык." +--- + + +2026.4.0 распространяет права внешних систем на Elasticsearch, Apache Doris, MongoDB, а также Redis и Valkey. Все четыре агента работают рядом с системой и согласуют её собственный контроль доступа с политиками. В части идентификации и авторизации добавлены синхронизация оргструктуры из DingTalk, прямые права пользователей, а также экспорт и импорт каталога ресурсов. Все изменения — в [сравнении на GitHub](https://github.com/devlive-community/grantforge/compare/v2026.3.0...v2026.4.0). + +## Elasticsearch + +- Плагин сервиса Elasticsearch: индексы и потоки данных, их поля и кластер; вход по паролю, ключу API или клиентскому сертификату TLS; см. [Elasticsearch](/ru/plugins/elasticsearch/). +- Агент рядом с кластером: ведёт для каждого пользователя собственную роль Elasticsearch, назначает её встроенным пользователям, а пользователям других realm — через сопоставление ролей; если позволяет лицензия, добавляет безопасность на уровне полей и документов; шаблоны индексов остаются шаблонами, поэтому новые индексы доступны сразу; см. [Агент Elasticsearch](/ru/external/elasticsearch-agent/). +- Записи о доступе берутся из файлов журнала аудита. Интеграционные тесты охватывают Elasticsearch 8.19, 9.3, 9.4 и 9.5. + +## Apache Doris + +- Плагин сервиса Doris: каталоги, базы данных, таблицы, столбцы и группы рабочей нагрузки; подключается к одному или нескольким FE и входит по паролю; см. [Apache Doris](/ru/plugins/doris/). +- Агент рядом с кластером: политики становятся собственными ролями Doris, правами на столбцы, правами на группы рабочей нагрузки и политиками строк и выдаются каждой идентичности пользователя; база данных или каталог целиком выдаются, только если Doris не даст этим больше; см. [Агент Apache Doris](/ru/external/doris-agent/). +- Записи о доступе берутся из таблицы журнала аудита Doris. Интеграционные тесты охватывают Doris 2.1, 3.1, 4.0 и 4.1. + +## MongoDB + +- Плагин сервиса MongoDB: базы данных и коллекции; подключается к набору реплик или шардированному кластеру и входит через SCRAM, клиентский сертификат, LDAP или Kerberos; см. [MongoDB](/ru/plugins/mongodb/). +- Агент рядом с развёртыванием: ведёт для каждого пользователя одну роль MongoDB и заменяет её привилегии за один шаг; по унаследованным привилегиям, которые сообщает MongoDB, показывает, что на деле дают встроенные и пользовательские роли вне GrantForge; см. [Агент MongoDB](/ru/external/mongodb-agent/). +- Записи о доступе берутся из файлов журнала аудита Enterprise или Percona либо из профилировщика. Интеграционные тесты охватывают MongoDB 6.0, 7.0, 8.0 и 9.0, Kerberos проверен на Percona Server for MongoDB. + +## Redis и Valkey + +- Плагин сервиса Redis: ключи, каналы и узлы; подключается к отдельному серверу с его репликами или к кластеру и входит как пользователь ACL, при желании по TLS; см. [Redis](/ru/plugins/redis/). +- Агент рядом с развёртыванием: на каждом узле согласует селекторы ACL каждого пользователя с политиками и заменяет их одной командой; корневые права по-прежнему ведут операторы, а агент спрашивает Redis через `ACL DRYRUN`, не разрешают ли они доступ, запрещённый политиками; см. [Агент Redis](/ru/external/redis-agent/). +- Записи об отказах берутся из журнала ACL каждого узла. Интеграционные тесты охватывают Redis 7.2, 7.4, 8.2 и 8.10, Valkey 8.1 и 9.1, а также кластер Redis 8.10. + +## Агенты + +- Агенты ClickHouse, PostgreSQL и OpenSearch и новые агенты этого выпуска, работающие рядом с системой, используют общее новое ядро `grantforge-agent-sync`: запуск, настройки, таймеры, цикл синхронизации, позиции чтения аудита и слежение за файлами журналов. Каждый агент оставляет себе только то, что относится к его системе; форматы настроек и журналов не изменились. + +## Идентификация и оргструктура + +- Источники данных: синхронизация оргструктуры настраивается отдельно от источников идентификации, первый адаптер — DingTalk на его официальном серверном SDK; после входа через SSO пользователи по доверенной почте автоматически привязываются к подразделениям и основному подразделению; таблицы мигрируют на всех шести поддерживаемых базах данных, включая SQL Server и Oracle; см. [Источники идентификации (LDAP и OIDC)](/ru/guide/identity-sources/). +- Источники идентификации OpenID Connect могут синхронизировать номер телефона и аватар и добавлять параметры в запрос авторизации (например, `provider_hint` для Casdoor); собственные параметры протокола переопределить нельзя. + +## Авторизация и каталог ресурсов + +- Прямые права пользователей: разрешают одному пользователю меню, страницы, кнопки или API бизнес-приложения, при необходимости до заданного срока; учитываются вместе с правами ролей, и запрет в роли по-прежнему важнее; см. [Пользователи](/ru/guide/users/). +- Каталог ресурсов может экспортировать дерево ресурсов приложения в JSON и импортировать его в другое приложение: импорт сначала проверяется, а при конфликте ничего не применяется; администраторы платформы могут очищать подходящие деревья приложений; см. [Каталог ресурсов и каталог API](/ru/guide/catalog/). + +## Сервер + +- При запуске сервер выводит баннер GrantForge со своей версией и версией Java вместо баннера Spring Boot. + +## Документация и выпуск + +- Вкладка плагинов открывается обзором, группирует системы по виду в сворачиваемой боковой панели и показывает для каждой системы способ применения, поддержку фильтров строк и маскирования и проверенные версии; все README перечисляют компоненты в том же порядке. +- CI проверяет, что каждая страница полностью переведена на каждый язык: те же абзацы, заголовки, таблицы, блоки кода и ссылки, что и на китайской странице, никакого текста в чужой письменности и ничего, скопированного с английской страницы; 11 README проходят те же проверки относительно английского README. Все недостающие переводы, найденные проверкой, восполнены. +- Проверка версий поддерживает актуальным значок версии в каждом README. +- Процессы публикации документации и выпуска больше не запускаются в форках. +- Сервер `grantforge-server` больше не публикуется в репозитории Maven: он включает все плагины и превышает их ограничение на размер; сервер поставляется в архиве выпуска и образе. + +## Исправления + +- При смене типа ресурса в редакторе ресурса очищается код ресурса, который больше не подходит. +- Последний блок кода в руководстве по интеграции на JavaScript больше не поглощает следующий за ним текст, а адрес консоли на страницах Docker и быстрого старта больше не превращает последующий текст в ссылку. +- Зависимости обновлены до версий, исправляющих уязвимости из бюллетеней безопасности: MariaDB Connector/J 3.5.11, OpenTelemetry 1.67.0, Guava 33.7.2, Snappy 1.1.10.9, а также vue-i18n, glob и minimatch в консоли и SDK. diff --git a/docs/content/ru/deploy/databases.md b/docs/content/ru/deploy/databases.md index 32706df4..28b2907c 100644 --- a/docs/content/ru/deploy/databases.md +++ b/docs/content/ru/deploy/databases.md @@ -31,7 +31,7 @@ GRANTFORGE_DB_URL=jdbc:postgresql://db:5432/grantforge GRANTFORGE_DB_URL=jdbc:mysql://db:3306/grantforge # MariaDB GRANTFORGE_DB_URL=jdbc:mariadb://db:3306/grantforge -# Oracle(服务名) +# Oracle (имя службы) GRANTFORGE_DB_URL=jdbc:oracle:thin:@//db:1521/FREEPDB1 # SQL Server GRANTFORGE_DB_URL=jdbc:sqlserver://db:1433;databaseName=grantforge;encrypt=true;trustServerCertificate=true diff --git a/docs/content/ru/deploy/docker.md b/docs/content/ru/deploy/docker.md index dcbefe5b..e13f7e5e 100644 --- a/docs/content/ru/deploy/docker.md +++ b/docs/content/ru/deploy/docker.md @@ -11,7 +11,7 @@ description: Запуск GrantForge в контейнерном образе, ## Образ -Каждый релиз публикует образ `ghcr.io/devlive-community/grantforge:<版本>` (linux/amd64 и linux/arm64); стабильные релизы также обновляют `latest`: +Каждый релиз публикует образ `ghcr.io/devlive-community/grantforge:<версия>` (linux/amd64 и linux/arm64); стабильные релизы также обновляют `latest`: ```bash docker run -p 9999:9999 ghcr.io/devlive-community/grantforge:2026.0.0 @@ -43,7 +43,7 @@ docker compose -f deploy/compose/postgres.yml up -d docker compose -f deploy/compose/postgres.yml logs grantforge | grep "setup token" ``` -Затем откройте http://127.0.0.1:9999/. Пароль базы данных по умолчанию, используемый в примерах, предназначен только для оценки; перед реальным использованием замените его через `GRANTFORGE_DB_PASSWORD`. Для примера MySQL сначала поместите `mysql-connector-j-<版本>.jar` в `deploy/compose/drivers/`. +Затем откройте . Пароль базы данных по умолчанию, используемый в примерах, предназначен только для оценки; перед реальным использованием замените его через `GRANTFORGE_DB_PASSWORD`. Для примера MySQL сначала поместите `mysql-connector-j-<версия>.jar` в `deploy/compose/drivers/`. ## Helm diff --git a/docs/content/ru/deploy/installation.md b/docs/content/ru/deploy/installation.md index ca07753a..7b66d405 100644 --- a/docs/content/ru/deploy/installation.md +++ b/docs/content/ru/deploy/installation.md @@ -36,10 +36,10 @@ description: Установка, запуск, остановка и обнов ## Запуск и остановка ```bash -bin/startup.sh # 后台启动,进程号写入 pid 文件 -bin/shutdown.sh # 按 pid 文件优雅停止 -bin/restart.sh # 停止后再启动 -bin/debug.sh # 前台运行,日志同时输出到控制台,Ctrl+C 停止 +bin/startup.sh # запуск в фоне, номер процесса записывается в pid-файл +bin/shutdown.sh # корректная остановка по pid-файлу +bin/restart.sh # остановить и снова запустить +bin/debug.sh # запуск на переднем плане, журнал выводится и в консоль; Ctrl+C останавливает ``` Скрипты можно запускать из любого каталога; каталогом установки считается родительский каталог того, в котором лежит скрипт, либо его можно задать явно переменной окружения `GRANTFORGE_HOME`. diff --git a/docs/content/ru/deploy/upgrade.md b/docs/content/ru/deploy/upgrade.md index ecb6c780..82895726 100644 --- a/docs/content/ru/deploy/upgrade.md +++ b/docs/content/ru/deploy/upgrade.md @@ -23,9 +23,9 @@ description: Обновление между версиями 2.x и мигра 1.x хранит данные в другом наборе таблиц, и 2.x их не читает. Путь миграции: установите 2.x на **новую базу данных** и завершите инициализацию, остановите сервис, затем импортируйте учётные записи, роли и меню из старой базы данных в арендатора (tenant): ```bash -# 先预演:只生成报告,不写入 +# сначала пробный прогон: только отчёт, без записи bin/import-legacy.sh --source-url jdbc:mysql://old-db:3306/authx --source-user authx --tenant default -# 确认报告后正式导入 +# после проверки отчёта выполнить импорт bin/import-legacy.sh --source-url jdbc:mysql://old-db:3306/authx --source-user authx --tenant default --apply ``` @@ -35,7 +35,7 @@ bin/import-legacy.sh --source-url jdbc:mysql://old-db:3306/authx --source-user a - **Учётные записи** сохраняют исходные пароли, которые при первом входе автоматически перехешируются новым алгоритмом. Записи, нарушающие правила имени пользователя в 2.x (3–64 буквы, цифры или `._@-`), без пароля или чьё имя уже занято другим арендатором, пропускаются. - **Роли** сохраняют имена; коды приводятся к нижнему регистру (`GLY` → `gly`). -- **Меню** становятся ресурсами приложения `legacy`: меню с адресами, содержащими `#`, становятся группировками, остальные адреса — страницами, а меню под страницей — кнопками. Адрес меню и его HTTP-метод превращаются в API-ресурс `api:<方法>:<路径>`; адреса, заканчивающиеся на `*`, становятся `<路径>/**` и сопоставляются по сегментам пути, а не по символьным префиксам — каждый такой случай перечислен в отчёте для проверки. +- **Меню** становятся ресурсами приложения `legacy`: меню с адресами, содержащими `#`, становятся группировками, остальные адреса — страницами, а меню под страницей — кнопками. Адрес меню и его HTTP-метод превращаются в API-ресурс `api:<метод>:<путь>`; адреса, заканчивающиеся на `*`, становятся `<путь>/**` и сопоставляются по сегментам пути, а не по символьным префиксам — каждый такой случай перечислен в отчёте для проверки. - Мигрируют только **явные разрешения**: в 1.x доступ к адресам, зарегистрированным как меню, имел любой пользователь, в 2.x — нет. > [!WARNING] diff --git a/docs/content/ru/develop/development.md b/docs/content/ru/develop/development.md index 2546b6c6..68d33451 100644 --- a/docs/content/ru/develop/development.md +++ b/docs/content/ru/develop/development.md @@ -19,13 +19,13 @@ description: Локальные сборки, тесты, стандарты к ## Основные команды ```bash -./mvnw verify # 构建并测试全部 Java 模块(含控制台) -./mvnw verify -DskipFrontend # 跳过控制台构建 -bash script/ci/web.sh test # 控制台单元测试 -bash script/ci/web.sh e2e # 控制台浏览器测试(模拟后端) -bash script/ci/e2e_fullstack.sh # 打包、启动真实服务并运行全栈测试 -bash script/ci/db_integration.sh postgres:17 # 在指定数据库上运行集成测试 -bash script/ci/perf_benchmark.sh smoke # 小规模性能基准 +./mvnw verify # собрать и протестировать все Java-модули (включая консоль) +./mvnw verify -DskipFrontend # пропустить сборку консоли +bash script/ci/web.sh test # модульные тесты консоли +bash script/ci/web.sh e2e # браузерные тесты консоли (с имитацией бэкенда) +bash script/ci/e2e_fullstack.sh # собрать пакет, запустить настоящий сервер и выполнить сквозные тесты +bash script/ci/db_integration.sh postgres:17 # интеграционные тесты на указанной базе данных +bash script/ci/perf_benchmark.sh smoke # небольшой тест производительности ``` Для разработки консоли запускайте `pnpm dev` в `core/grantforge-web`; Vite проксирует `/api` и другие запросы на сервер `localhost:9999`. @@ -68,15 +68,15 @@ bash script/ci/perf_benchmark.sh smoke # 小规模性能基准 Релиз из ветки `dev` выполняется одной командой: ```bash -bash script/release/tag.sh 2026.1.0 --dry-run # 只检查并预览发布说明,不做任何修改 -bash script/release/tag.sh 2026.1.0 --next 2026.2.0 # 设置版本、打标签 v2026.1.0 并推送,再把 dev 切到下一个版本 +bash script/release/tag.sh 2026.1.0 --dry-run # только проверить и показать примечания к выпуску, ничего не менять +bash script/release/tag.sh 2026.1.0 --next 2026.2.0 # задать версию, поставить тег v2026.1.0 и отправить, затем перевести dev на следующую версию ``` Скрипт требует чистое рабочее дерево, локальную ветку, не отстающую от удалённой, и несуществующий тег; после подтверждения он создаёт коммит `chore(release): prepare `, навешивает аннотированный тег и отправляет его. Тег запускает `release.yml`: - `script/ci/release.sh` собирает дистрибутив, SBOM CycloneDX, содержащий только зависимости релиза, и `SHA256SUMS`; - мультиархитектурные образы отправляются в `ghcr.io/devlive-community/grantforge`; -- артефакты Maven (включая исходный код и Javadoc) публикуются в GitHub Packages; если репозиторий настроен через `CENTRAL_USERNAME`, `CENTRAL_PASSWORD` (токен Central Portal), `GPG_PRIVATE_KEY` и `GPG_PASSPHRASE`, они подписываются и публикуются в Maven Central; +- артефакты Maven (включая исходный код и Javadoc) публикуются в GitHub Packages; если репозиторий настроен через `CENTRAL_USERNAME`, `CENTRAL_PASSWORD` (токен Central Portal), `GPG_PRIVATE_KEY` и `GPG_PASSPHRASE`, они подписываются и публикуются в Maven Central; сервер `grantforge-server` включает все плагины и превышает ограничение репозиториев на размер, поэтому он не публикуется ни в один репозиторий Maven и поставляется только в архиве выпуска и образе; - создаётся GitHub Release, в описании которого перечислены все коммиты с предыдущего релиза (`v*` или числовый тег, например `1.0.6`), сгруппированные по возможностям, исправлениям, производительности и т. д., со ссылками на коммиты. Кандидаты в релиз помечаются как pre-release и не обновляют тег `latest` образов. Локально профиль `central` по умолчанию ничего не публикует (`central.skip=true`); только релизный workflow явно передаёт `-Dcentral.skip=false`. @@ -95,8 +95,10 @@ bash script/release/tag.sh 2026.1.0 --next 2026.2.0 # 设置版本、打标 cd docs pnpm install pnpm dev # http://localhost:3100 -pnpm check # 页面、链接与图片检查 -pnpm build # 输出到 docs/out +pnpm check # проверка страниц, ссылок и изображений +pnpm build # результат в docs/out ``` Страницы — это Markdown в `docs/content/`, а навигация определена в `docs/lib/navigation.ts`. Справочник API и коды ошибок генерируются при сборке из контракта и исходного кода. Скриншоты создаёт `script/docs/screenshots.sh`, который запускает реальный сервер, наполняет его тестовыми данными и управляет Playwright. + +`pnpm test` также проверяет переводы: у каждой китайской страницы должен быть перевод на каждый из остальных десяти языков с теми же абзацами, уровнями заголовков, списками, таблицами, блоками кода и ссылками. На странице не должно быть текста в письменности, которой этот язык не пользуется (включая упрощённые иероглифы в традиционном китайском и японском), и предложений, скопированных без изменений с английской страницы. Те же проверки проходят 11 README в корне репозитория относительно английского README, а их ссылки на этот сайт должны вести на существующие страницы своего языка. diff --git a/docs/content/ru/develop/plugins.md b/docs/content/ru/develop/plugins.md index 6e345f61..31b7afc7 100644 --- a/docs/content/ru/develop/plugins.md +++ b/docs/content/ru/develop/plugins.md @@ -59,7 +59,7 @@ public final class ExampleProvider implements ServiceTypeProvider @Override public List lookup(LookupRequest request) { - // 返回 request.resource() 层级下以 request.userInput() 开头的候选值,最多 request.limit() 个 + // Вернуть кандидатов ниже request.resource(), начинающихся с request.userInput(), не больше request.limit() return List.of(); } } @@ -185,7 +185,7 @@ AgentSettings settings = AgentSettings.builder() .instance("namenode-1:8020") .cacheDirectory(Paths.get("/var/lib/grantforge-agent")) .build(); -GrantForgeAgent agent = GrantForgeAgent.start(settings, evaluators); // 条件求值器,按名称 +GrantForgeAgent agent = GrantForgeAgent.start(settings, evaluators); // вычислители условий по имени AgentDecision decision = agent.decide(AccessRequest.builder(user, "read").groups(groups).resource("path", path).build()); agent.record(AccessEvent.builder(user, path, "read", allowed).decidedBy(decision).action("open").build()); diff --git a/docs/content/ru/external/hdfs-agent.md b/docs/content/ru/external/hdfs-agent.md index be14ca7a..c82e9089 100644 --- a/docs/content/ru/external/hdfs-agent.md +++ b/docs/content/ru/external/hdfs-agent.md @@ -9,71 +9,69 @@ description: Применение политик GrantForge агентом NameN project root for full license text. --> -Серверный плагин `grantforge-plugin-hdfs` задаёт ресурсы и подключения. Нумерованные агенты NameNode для Hadoop 2.7, 2.10, 3.2, 3.3, 3.4 и 3.5 проверяют доступ через SPI своей версии и разделяют логику подписанных снимков, оценки политик и отправки аудита. +Серверный плагин `grantforge-plugin-hdfs` единообразно предоставляет тип сервиса `hdfs`, ресурсы и настройки подключения. Агенты NameNode собираются отдельно для каждой версии Hadoop, проверяют доступ через `INodeAttributeProvider` и `AccessControlEnforcer` своей версии и используют общую логику подписанных политик, кэша, вычисления политик и аудита. -Общая логика HDFS находится в `agents/grantforge-agent-hdfs-common`, адаптеры версий — в `agents/grantforge-agent-hdfs-`. Общие нативные адаптеры входят в производственный Maven-модуль `agents/grantforge-agent-hdfs-native` на базе Java 8 / Hadoop 2.7.7. Нумерованные модули используют его бинарную Maven-зависимость и сохраняют свои точки входа и специфичные для версии callbacks, не перекомпилируя общие производственные исходники. `core/grantforge-agent-core` остаётся общей библиотекой протокола и выполнения. +В исходном коде серверный плагин находится в `plugins/grantforge-plugin-hdfs`, версионные агенты — в `agents/grantforge-agent-hdfs-`, а общая рабочая логика без зависимости от Hadoop — в `agents/grantforge-agent-hdfs-common`. Общий нативный адаптер находится в полноценном рабочем модуле Maven `agents/grantforge-agent-hdfs-native` и рассчитан на Java 8 / Hadoop 2.7.7; нумерованные модули подключают его как бинарную зависимость Maven, сохраняют свои точки входа и особые обратные вызовы и не компилируют общий рабочий код повторно. Протокол, кэш снимков и инфраструктура аудита по-прежнему находятся в `core/grantforge-agent-core`. -Серверный плагин сохраняет единый тип службы `hdfs`, а версия клиента не зависит от агентов. Для подключения и поиска путей в Hadoop 2.x используйте `webhdfs://namenode:50070` или соответствующий адрес `swebhdfs://` с HTTPS. Hadoop 3.x может использовать RPC `hdfs://` или WebHDFS. Контейнерные тесты проверяют WebHDFS на 2.x и оба протокола на 3.x. RPC на 2.x не сертифицирован; настроенный протокол не меняется автоматически. +Серверный плагин сохраняет один тип `hdfs`, а версия клиента не зависит от версии агента. Для подключения и поиска путей в Hadoop 2.x используется `webhdfs://namenode:50070` (при включённом HTTPS — соответствующий адрес `swebhdfs://`); в Hadoop 3.x можно использовать RPC `hdfs://` или WebHDFS. Контейнерная матрица проверяет WebHDFS для 2.x, а также RPC и WebHDFS для 3.x; RPC для 2.x не сертифицирован, и протокол подключения автоматически не переключается. -При включённых расширениях атрибутов inode Apache Hadoop 2.7.7 вызывает нативную ошибку нулевого указателя при запросе `/` обычным пользователем, до вызова агента. Для операций с данными и `lookup.path` в этой версии используйте каталоги данных, например `/data`. Контейнерный тест явно проверяет это ограничение корневого пути. +В Apache Hadoop 2.7.7 с включённым расширением атрибутов inode прямой запрос корневого пути `/` обычным пользователем вызывает в нативном коде ошибку нулевого указателя ещё до обратного вызова агента. Для операций с данными и `lookup.path` сервиса в этой версии используйте реальные каталоги данных, например `/data`; в тестах сохранена отдельная проверка сбоя на корневом пути. -## Целевые версии агентов HDFS +## Выбор версии -| Базовая версия Hadoop | Java контейнера | Каталог агента | -| --- | --- | --- | -| 2.7.7 | Java 8 | `agents/hdfs/2.7/` | -| 2.10.2 | Java 8 | `agents/hdfs/2.10/` | -| 3.2.4 | Java 8 | `agents/hdfs/3.2/` | -| 3.3.6 | Java 8 (образ amd64) | `agents/hdfs/3.3/` | -| 3.4.3 | Java 11 | `agents/hdfs/3.4/` | -| 3.5.0 | Java 17 | `agents/hdfs/3.5/` | - -Выберите `grantforge-agent-hdfs--.jar` из `agents/hdfs//` для ветки Hadoop вашего кластера. Общая логика рассчитана на Java 8, адаптер 3.5 — на Java 17. +| Суффикс модуля агента | Проверенный Apache Hadoop | JVM контейнера | Интерфейс авторизации | Обратный вызов пути суперпользователя | +| --- | --- | --- | --- | --- | +| `2.7` | 2.7.7 | Java 8 | с параметрами | нет | +| `2.10` | 2.10.2 | Java 8 | с параметрами | нет | +| `3.2` | 3.2.4 | Java 8 | с параметрами | нет | +| `3.3` | 3.3.6 | Java 8 | с контекстом | нет | +| `3.4` | 3.4.3 | Java 11 | с контекстом, обратные вызовы суперпользователя и отказа | есть | +| `3.5` | 3.5.0 | Java 17 | с контекстом, обратные вызовы суперпользователя и отказа | есть | -Hadoop 2.7, 2.10, 3.2 и 3.3 не предоставляют используемый агентом callback авторизации суперпользователя. Hadoop сохраняет контроль этих обращений; для данных, регулируемых политиками GrantForge, используйте обычных пользователей. +Например, для Hadoop 2.10.2 устанавливается `agents/hdfs/2.10/grantforge-agent-hdfs-2.10-.jar`. На каждый NameNode ставится только один версионный агент; при обновлении старый jar удаляется, а имя класса provider в конфигурации не меняется. При запуске агент сверяет основную и дополнительную версии Hadoop и обязательные обратные вызовы и не запускается, если установлена не та версия; другие патчи той же линии и сборки поставщиков по-прежнему требуют отдельной проверки. -## Соотношение с встроенными правами +Основной код агентов `common` и `2.7`–`3.4` компилируется в байт-код Java 8, а `3.5` — в Java 17. В таблице указаны JVM, на которых реально проходят интеграционные тесты; это не означает, что 3.4.3 проверен на Java 8. Официальный тестовый образ 3.3.6 есть только для amd64, поэтому на хостах ARM он явно запускается в эмуляции amd64. -Следующее поведение относится к обращениям обычных пользователей. Ограничения суперпользователя описаны выше и в разделе его callbacks. +## Связь с нативными правами -Агент сначала выполняет встроенные проверки прав HDFS, затем — политики GrantForge: пользователь должен удовлетворять и тем, и другим. Разрешающие политики GrantForge не отменяют POSIX-права, ACL, проверку владельца или sticky bit; запрещающие политики всегда запрещают. Встроенные настройки прав по-прежнему администрируются средствами Hadoop. +Агент сначала выполняет нативную проверку прав HDFS, а затем применяет политики GrantForge: пользователь должен удовлетворять и нативным правам, и требованиям политик. Разрешающие политики GrantForge не обходят права POSIX, ACL, проверку владельца и sticky bit; запрещающие политики всегда запрещают. Нативные права по-прежнему ведутся средствами администрирования Hadoop. -Значение по умолчанию `grantforge.hdfs.native.fallback=false` запрещает доступ к данным, когда нет локального снимка политики, нет подходящей политики или агент ещё не запущен. При `true` доступ, не решённый политиками, определяется встроенными правами; явные запрещающие политики действуют независимо от этого. Пока сервер временно недоступен, агент продолжает использовать последний локальный снимок, прошедший проверку подписи. +По умолчанию `grantforge.hdfs.native.fallback=false`: если нет локального снимка политик, нет подходящей политики или агент ещё не запущен, доступ к данным запрещается. При значении `true` доступ, не решённый политиками, определяется нативными правами; явные запрещающие политики продолжают действовать. Если сервер временно недоступен, используется последний локальный снимок, прошедший проверку подписи. -Предки, цель, поддеревья и проекции путей снимка внутри одного обратного вызова авторизации используют одну и ту же версию снимка политики; обновлённые политики вступают в силу со следующего вызова, поэтому правила разрешения из разных версий никогда не смешиваются. Записи аудита доступа фиксируют фактически использованную версию политики. +Проекции предков, цели, поддеревьев и путей снимков в рамках одного обратного вызова авторизации используют одну и ту же версию снимка политик; обновлённые политики вступают в силу со следующего обратного вызова, поэтому разрешающие правила разных версий не смешиваются. В аудите доступа записывается фактически использованная версия политик. -Агент проверяет права `read`, `write` и `execute`, которые нужны обычному пользователю для цели, а также родительские каталоги, каталоги-предки и подкаталоги, требующие рекурсивной проверки. Операции создания, удаления и переименования затрагивают несколько путей, и разрешающие политики должны покрывать все их. В строгом режиме одной политики `read` на целевой файл недостаточно: пользователю также нужны политики `execute` на каталогах-предках — например, разрешите `execute` на `/` с включённой рекурсией, а затем настройте чтение и запись на реальных каталогах данных. +Агент проверяет `read`, `write` и `execute`, нужные обычному пользователю для доступа к цели, а также родительский каталог, каталоги-предки и подкаталоги, требующие рекурсивной проверки. Создание, удаление, переименование и другие операции затрагивают несколько путей, и разрешающие политики должны покрывать их все. В строгом режиме одной политики `read` на целевой файл недостаточно: пользователю нужна политика `execute` на каталоги-предки, например разрешить `execute` на `/` с рекурсией, а затем задать права на чтение и запись для реальных каталогов данных. -Пути снимков проверяются и как фактически запрошенный путь, и как исходный путь с удалённым `.snapshot/` — например, `/data/.snapshot/s1/secret` также проверяется как `/data/secret`. Поэтому запрещающие политики для исходного пути ограничивают и снимки; для явных путей снимков можно дополнительно настроить более строгие ограничения. Запросы метаданных следуют семантике прав при обходе каталогов HDFS. +Для путей снимков проверяются и фактически запрошенный путь, и исходный путь без `.snapshot/<имя снимка>`: например, для `/data/.snapshot/s1/secret` проверяется и `/data/secret`. Поэтому запрещающие политики исходного пути распространяются и на снимки; для явных путей снимков можно задать дополнительные, более строгие ограничения. Запросы метаданных следуют семантике прав обхода каталогов HDFS. -Одна рекурсивная авторизация проверяет не более `100000` инодов; при превышении предела операция запрещается, чтобы избежать неограниченного выделения памяти внутри NameNode. Слишком длинные пути оцениваются по полному пути; отображение ресурса в аудите ограничено `1000` символами, а исходная длина и дайджест SHA-256 записываются в деталях запроса. +Одна рекурсивная авторизация проверяет не более `100000` inode; при превышении операция запрещается, чтобы не выделять память в NameNode без ограничений. Для очень длинных путей политика определяется по полному пути; отображение ресурса в аудите ограничено `1000` символами, а в деталях запроса записываются исходная длина и дайджест SHA-256. -Следующие callbacks суперпользователя относятся к Hadoop 3.4 и 3.5. Суперпользователи HDFS по-прежнему администрируются Hadoop. Обратные вызовы суперпользователя с путём сначала проходят проверку суперпользователя Hadoop, затем проверяются по политикам согласно именам операций, предоставляемым Hadoop 3.4 / 3.5: чтение файлов и запросы метаданных требуют `read`, перечисление каталогов — `read` + `execute`, а известные операции изменения — `write`. Неизвестные, отсутствующие или ненадёжно определяемые операции (например, `checkAccess` и `concat`) консервативно требуют всех трёх прав. +Hadoop 2.7, 2.10, 3.2 и 3.3 пропускают суперпользователей ещё до вызова агента, поэтому агент не может ни контролировать, ни аудировать такой доступ. В Hadoop 3.4 и 3.5 обратный вызов суперпользователя с путём сначала проходит нативную проверку, а затем политики проверяются по имени операции: чтение файлов и запросы метаданных требуют `read`, перечисление каталогов — `read` + `execute`, известные изменяющие операции — `write`. Неизвестные, отсутствующие или не поддающиеся точному определению операции (например, `checkAccess` и `concat`) консервативно требуют всех трёх прав. -У обратных вызовов суперпользователя нет полного контекста инодов и поддеревьев, а вызовы администрирования кластера без пути сохраняют встроенные проверки; ни одну рекурсивную операцию суперпользователя нельзя ограничить политиками подкаталогов. Потребителям данных следует пользоваться обычными пользователями Hadoop. +У обратных вызовов суперпользователя нет полного контекста inode и поддеревьев, поэтому вызовы управления кластером без пути остаются на нативной проверке; ограничить политиками подкаталогов все рекурсивные операции суперпользователя нельзя. Потребителям данных следует использовать обычных пользователей Hadoop. ## Метрики -Агент сообщает свои метрики через систему Metrics2 Hadoop, по тем же sink'ам, что и собственные dfs-метрики NameNode: в JMX NameNode они зарегистрированы как `Hadoop:service=NameNode,name=GrantForgeHdfsAgent` (Prometheus JMX exporter может собирать их напрямую). Каждая метрика несёт теги `instance` (экземпляр сервиса данных) и `agentVersion`, поэтому два NameNode в паре HA можно различить. Неудачная регистрация стоит только метрик: агент пишет предупреждение и продолжает принуждение без них. +Агент публикует метрики через систему Metrics2 Hadoop и использует тот же sink, что и собственные метрики dfs NameNode; в JMX NameNode это `Hadoop:service=NameNode,name=GrantForgeHdfsAgent` (экспортёр JMX для Prometheus может собирать их напрямую). У каждой метрики есть метки `instance` (имя экземпляра сервиса данных) и `agentVersion`, поэтому два NameNode в HA можно просматривать по отдельности. Сбой регистрации метрик теряет только сами метрики: агент записывает предупреждение и продолжает авторизацию без них. -| Метрика | Значение | +| Метрика | Описание | | --- | --- | -| `Callbacks` | Обратные вызовы авторизации, обработанные агентом | -| `SuperuserCallbacks` | Обратные вызовы авторизации суперпользователя, обработанные агентом (3.4 / 3.5) | -| `NativeDenies` | Попытки доступа, отклонённые Hadoop до запуска агента | -| `EvaluationFailures` | Вызовы, завершённые отказом, из-за исключения при оценке политики | -| `DecisionsAllowed` / `DecisionsDenied` / `DecisionsUndetermined` | Разрешённые / отклонённые / оставленные без решения политикой права; в строгом режиме «без решения» тоже отклоняется | -| `MissingSnapshots` | Обслуженные вызовы в отсутствие проверенного снимка политики | -| `SnapshotVersion` | Версия политики используемого снимка, 0, если снимка нет | -| `QueuedEvents` / `DroppedEvents` | События доступа, ожидающие в памяти / потерянные из-за заполнения очереди или дискового буфера | -| `ServerReachable` | Успешен ли последний вызов сервера политик (1/0) | +| `Callbacks` | Число обратных вызовов авторизации, выполненных агентом | +| `SuperuserCallbacks` | Число обратных вызовов суперпользователя, выполненных агентом | +| `NativeDenies` | Число обращений, запрещённых Hadoop ещё до агента | +| `EvaluationFailures` | Число обратных вызовов, завершившихся запретом из-за исключения при вычислении политик | +| `DecisionsAllowed` / `DecisionsDenied` / `DecisionsUndetermined` | Число прав, которые политики разрешили / запретили / оставили нерешёнными; нерешённые в строгом режиме тоже запрещаются | +| `MissingSnapshots` | Число обратных вызовов, обслуженных без проверенного снимка политик | +| `SnapshotVersion` | Текущая версия снимка политик; 0 — снимка нет | +| `QueuedEvents` / `DroppedEvents` | Число событий аудита, ожидающих отправки в памяти / отброшенных из-за заполненной очереди или дискового буфера | +| `ServerReachable` | Было ли успешным последнее обращение к серверу политик (1/0) | ## Развёртывание -1. Добавьте сервис `hdfs` в разделе сервисов данных GrantForge, сохраните конфигурацию и проверьте подключение; настройте политики путей для реальных коротких имён пользователей Hadoop, групп или ролей. -2. В разделе «Права на данные → Агенты» выпустите токен для этого сервиса. Запишите сырой токен в локальный файл на каждом NameNode, например `/etc/hadoop/grantforge/token`, доступный для чтения пользователю, от имени которого работает NameNode. -3. Выберите ветку Hadoop кластера и скопируйте `grantforge-agent-hdfs--.jar` из `agents/hdfs//` в classpath NameNode, например `$HADOOP_HOME/share/hadoop/hdfs/lib/`. Установите только один подходящий адаптер. Jar включает общую логику, Jackson и библиотеку подписей; Hadoop предоставляется NameNode. -4. Настройте следующие свойства в `hdfs-site.xml` каждого NameNode; два NameNode в конфигурации HA используют разные значения `instance` и собственные каталоги локального кэша. +1. Добавьте сервис `hdfs` в сервисы данных GrantForge, сохраните настройки и проверьте подключение; настройте политики путей для реальных коротких имён пользователей Hadoop, групп или ролей. +2. В разделе «Права на данные → Агенты» выпустите токен для этого сервиса. Запишите текст токена в локальный файл на каждом NameNode, например `/etc/hadoop/grantforge/token`, доступный для чтения пользователю, от имени которого работает NameNode. +3. По таблице выберите jar агента из `agents/hdfs/<линия Hadoop>/` в дистрибутиве и поместите его в classpath NameNode, например в `$HADOOP_HOME/share/hadoop/hdfs/lib/`. Jar агента содержит собственный движок политик, Jackson и библиотеку подписи, а классы Hadoop предоставляет NameNode; версия агента в heartbeat включает версию продукта и версию Hadoop, для которой он собран. +4. Задайте в `hdfs-site.xml` каждого NameNode следующие свойства; два NameNode в HA используют разные `instance` и собственные локальные каталоги кэша. ```xml @@ -102,58 +100,56 @@ Hadoop 2.7, 2.10, 3.2 и 3.3 не предоставляют используе ``` -5. Убедитесь, что `dfs.permissions.enabled=true`, а `dfs.namenode.inode.attributes.provider.bypass.users` пуст; при запуске агент отклоняет конфигурации, позволяющие обходить обратные вызовы авторизации. Перезапустите NameNode, затем проверьте heartbeat и версию политики на странице агентов GrantForge. Агент читает существующую конфигурацию NameNode; он не изменяет встроенные атрибуты инодов. +5. Убедитесь, что `dfs.permissions.enabled=true`, а `dfs.namenode.inode.attributes.provider.bypass.users` пусто; при конфигурации, позволяющей обойти обратный вызов авторизации, агент не запустится. Перезапустите NameNode и проверьте heartbeat и версию политик на странице агентов GrantForge. Агент читает существующую конфигурацию NameNode и не меняет нативные атрибуты inode. -Для первого развёртывания можно начать с `native.fallback=true`, убедиться, что снимок политики синхронизирован, а права каталогов-предков заданы полностью, и лишь затем перейти в строгий режим. Тип сервиса, с которым связан токен, должен быть `hdfs`; при ошибке конфигурации или привязке к другому типу сервиса доступ запрещается. +При первом развёртывании можно начать с `native.fallback=true`, убедиться, что снимок политик синхронизирован, и дополнить права на каталоги-предки, а затем перейти в строгий режим. Токен должен быть привязан к типу сервиса `hdfs`; при ошибке конфигурации или привязке к другому типу сервиса доступ запрещается. -## Дополнительные настройки +## Необязательные настройки | Свойство | По умолчанию | Назначение | | --- | --- | --- | | `grantforge.hdfs.connect.timeout.ms` | `5000` | Тайм-аут подключения к GrantForge | | `grantforge.hdfs.read.timeout.ms` | `8000` | Тайм-аут чтения ответа | -| `grantforge.hdfs.refresh.interval.ms` | `30000` | Интервал обновления политики, пока сервер недоступен, не менее `1000`; обычные heartbeat'ы используют интервал, предложенный сервером | -| `grantforge.hdfs.signing.key.file` | не задан | Необязательный файл открытого ключа подписи с открытым ключом Base64 X.509, выданным консолью; при настройке принимаются только подписи этим ключом | -| `grantforge.hdfs.audit.batch.size` | `500` | Максимальное число событий в одном отчёте, диапазон `1..1000` | -| `grantforge.hdfs.audit.queue.capacity` | `10000` | Ёмкость очереди аудита в памяти, диапазон `1..1000000`, должна вмещать хотя бы один пакет; при заполнении очереди новые события подсчитываются и отбрасываются | -| `grantforge.hdfs.audit.flush.interval.ms` | `5000` | Интервал сброса аудита, положительное целое число, не более `2147483647`; уменьшите его, чтобы снизить задержку отправки | -| `grantforge.hdfs.audit.spool.limit.bytes` | `67108864` | Предел дискового буфера, пока сервер недоступен, неотрицательное целое число; `0` отключает дисковый буфер | - -При всплесках нагрузки увеличьте очередь аудита, чтобы уменьшить переполнение; уменьшение интервала сброса снижает задержку аудита, но увеличивает частоту отправки. Предел дискового буфера контролирует использование диска при длительных отключениях; события могут теряться, если буфер отключён или исчерпан. Отправка аудита выполняется в фоновом режиме и не ждёт ответа сервера политик. +| `grantforge.hdfs.refresh.interval.ms` | `30000` | Интервал обновления политик, пока сервер недоступен, не меньше `1000`; обычный heartbeat использует интервал, рекомендованный сервером | +| `grantforge.hdfs.signing.key.file` | не задано | Необязательный файл открытого ключа подписи с открытым ключом X.509 в Base64 из консоли; если он задан, принимаются только подписи этим ключом | +| `grantforge.hdfs.audit.batch.size` | `500` | Наибольшее число событий в одной отправке, диапазон `1..1000` | +| `grantforge.hdfs.audit.queue.capacity` | `10000` | Ёмкость очереди аудита в памяти, диапазон `1..1000000`, не меньше одного пакета; при заполнении новые события подсчитываются и отбрасываются | +| `grantforge.hdfs.audit.flush.interval.ms` | `5000` | Интервал сброса аудита, положительное целое, не больше `2147483647`; меньшее значение уменьшает задержку отправки | +| `grantforge.hdfs.audit.spool.limit.bytes` | `67108864` | Предел дискового буфера, пока сервер недоступен, неотрицательное целое; `0` отключает дисковый буфер | -Если открытый ключ подписи не настроен, агент при первом обращении запрашивает его у сервера и хранит вместе со снимком. Агент использует короткое имя пользователя и группы, переданные Hadoop; роли и дополнительные группы поступают из подписанного снимка. Сопоставление коротких имён для principal'ов Kerberos определяется `hadoop.security.auth_to_local` кластера. +При всплесках нагрузки можно увеличить очередь аудита, чтобы она реже переполнялась; более короткий интервал сброса уменьшает задержку аудита, но учащает отправку. Предел дискового буфера ограничивает занятое место на диске при длительной потере связи; если буфер отключён или исчерпан, события могут потеряться. Аудит отправляется в фоне и не ждёт ответа сервера политик. -## Сборка и проверка из исходников +Если открытый ключ подписи не настроен, агент при первом обращении получает его с сервера и сохраняет вместе со снимком. Агент использует короткое имя пользователя и группы, переданные Hadoop, а роли и дополнительные группы берёт из подписанного снимка; сопоставление principal Kerberos с коротким именем определяет `hadoop.security.auth_to_local` кластера. -Команды показывают 3.5; замените ветку на 2.7, 2.10, 3.2, 3.3 или 3.4 для нужного адаптера. +## Сборка из исходников и проверка ```sh ./mvnw -pl agents/grantforge-agent-hdfs-3.5 -am package ./mvnw -pl plugins/grantforge-plugin-hdfs,agents/grantforge-agent-hdfs-3.5,core/grantforge-plugin-host -am test ``` -Артефакт агента оказывается в `agents/grantforge-agent-hdfs-3.5/target/grantforge-agent-hdfs-3.5-.jar`. Юнит-тесты покрывают обратные вызовы авторизации NameNode, конфигурацию, метаданные версии и решения по политикам; тесты WebHDFS и Kerberos запускают временные локальные сервисы. Перед выходом в продакшен проверьте на целевом кластере также чтение и запись, создание, переименование, рекурсивное удаление, переключение HA и поведение кэша после сетевого сбоя. +Артефакт агента находится в `agents/grantforge-agent-hdfs-3.5/target/grantforge-agent-hdfs-3.5-.jar`. Модульные тесты покрывают обратные вызовы авторизации NameNode, конфигурацию, метаданные версий и решения политик; тесты WebHDFS и Kerberos запускают временные локальные сервисы. Перед вводом в эксплуатацию также проверьте на целевом кластере чтение и запись, создание, переименование, рекурсивное удаление, переключение HA и поведение кэша после потери связи. -Интеграционная проверка выполняется фазой `verify` с использованием Testcontainers (юнит-тесты не запускают кластер; `verify` требует работающего демона Docker): +Интеграционная проверка выполняется с Testcontainers на фазе `verify` (модульные тесты кластер не запускают; для `verify` нужен работающий демон Docker): ```sh ./mvnw -pl agents/grantforge-agent-hdfs-3.5 -am verify -# Та же точка входа, что и в nightly -bash script/ci/hdfs_integration.sh 3.5 -# Одна ветка или полная матрица -bash script/ci/hdfs_integration.sh all +# та же точка входа, что и в nightly +bash script/ci/hdfs_integration.sh +# проверить только указанную линию версий +bash script/ci/hdfs_integration.sh 2.10 ``` -Тесты используют контейнерные образы Apache Hadoop с зафиксированными версиями и помещают фактически упакованный jar агента в classpath NameNode. Testcontainers создаёт изолированную сеть и управляет жизненным циклом NameNode и DataNode, проверяя чтение и запись, создание, дозапись, переименование, удаление, рекурсивный запрет и запрет снимков, встроенные права и аудит, обновление политик, перезапуск NameNode с подписанным кэшем после отключения сервера политик, а также строгий режим с откатом на встроенные права при отсутствии снимка. +Тесты используют образы Apache Hadoop фиксированных версий; образы старых версий собираются из базового образа Java 8 с фиксированным дайджестом и дистрибутива Apache, проверенного по SHA-512. Настоящий jar агента нужной версии помещается в classpath NameNode, а фактические версии Hadoop и JVM проверяются утверждениями. Testcontainers создаёт изолированную сеть и управляет жизненным циклом NameNode и DataNode; проверяются чтение и запись, создание, дозапись, переименование, удаление, запреты для рекурсии и снимков, нативные права и аудит, обновление политик, перезапуск NameNode с подписанным кэшем после отключения сервера политик, а также строгий режим и откат к нативным правам при отсутствии снимка. -Требуется работающий Docker-демон, и должно быть разрешено скачивание тестовых образов. Если Docker недоступен, тесты завершаются ошибкой; они никогда не пропускаются молча. Файловый клиент работает внутри контейнера Hadoop, а HTTP-служба политик использует проброс портов Testcontainers на хост; внешний кластер Hadoop не нужен. Контейнеры и тестовая сеть удаляются после тестов, а логи сохраняются в `agents/grantforge-agent-hdfs-3.5/target/hdfs-testcontainers`. +Нужен работающий демон Docker и разрешение на загрузку тестовых образов. Если Docker недоступен, тесты падают, а не пропускаются молча. Клиент файловой системы работает внутри контейнера Hadoop, а HTTP-сервис политик доступен через проброс портов хоста Testcontainers; внешний кластер Hadoop не нужен. После тестов контейнеры и тестовая сеть удаляются, а журналы сохраняются в `agents/grantforge-agent-hdfs-3.5/target/hdfs-testcontainers`. -HA-тест запускает два NameNode, один DataNode и один JournalNode и настраивает независимые имена экземпляров и каталоги кэша для обоих агентов. Он использует логический клиент HDFS, чтобы вручную переключить активный узел, и проверяет чтение, запись и запрет политик после переключения. Единственный JournalNode существует только для тестирования: отказоустойчивость кворума не проверяется, автоматическое переключение на основе ZooKeeper не используется. +Тест HA запускает два NameNode, один DataNode и один JournalNode и задаёт двум агентам отдельные имена экземпляров и каталоги кэша. Он вручную переключает активный узел через логический клиент HDFS и проверяет чтение, запись и запрещающие политики после переключения; один JournalNode используется только для теста, поэтому отказоустойчивость по большинству не проверяется, а автоматическое переключение через ZooKeeper не задействуется. -Тест автоматического HA (Hadoop с 2.10 по 3.5, Java 17 и 21) запускает ZooKeeper, три JournalNode, два NameNode, каждый со своим ZKFC, и один DataNode. Проверяется, что после аварийной остановки активного NameNode ZooKeeper передаёт роль другому, который продолжает применять политики и записывает отказы под своим экземпляром; что политики, опубликованные во время отсутствия NameNode, применяются, когда он возвращается как standby, и продолжают действовать после возврата роли, без отката к старой версии; что без сервера политик принявший роль NameNode продолжает работать по имеющемуся снимку и отправляет аудит после возвращения сервера; и что при остановке одного из трёх JournalNode запись проходит, а при остановке двух запись завершается ошибкой и активный NameNode останавливается, а не продолжает работу без кворума. Вариант с Kerberos, только на Hadoop 3.5.0, добавляет JournalNode, которые входят по keytab и работают только по HTTPS, ZKFC, которые аутентифицируются в ZooKeeper через SASL, и znode выборов, доступные только principal NameNode (неаутентифицированный клиент ZooKeeper не может их даже прочитать); проверяется, что при остановке активного NameNode роль принимает другой и продолжает применять политики к пользователям Kerberos, что клиент без билета всегда получает отказ и что роль возвращается, когда первый NameNode снова доступен. JournalNode работают под principal с коротким именем NameNode, потому что JournalNode отдаёт edits только полному principal NameNode или запрашивающему с собственным коротким именем, а HTTP-аутентификация передаёт ему только короткие имена. Автоматический HA на Hadoop 2.7 не проверен: его клиент переключается, только пока разрешается имя хоста каждого NameNode, а имя остановленного контейнера перестаёт разрешаться, поэтому клиент завершается с UnknownHostException, не пробуя другой NameNode. +Тест автоматического HA (Hadoop 2.10–3.5, Java 17 и 21) запускает ZooKeeper, три JournalNode, два NameNode с собственными ZKFC и один DataNode. Тест проверяет, что после прямой остановки активного NameNode ZooKeeper выбирает другой узел, новый активный узел продолжает применять политики, а события запрета относятся к его экземпляру; новые политики, опубликованные, пока узел отсутствовал, вступают в силу после его возвращения в роли Standby, и после обратного переключения применяется новая версия, а не старая; пока сервис политик недоступен, принявший работу узел продолжает применять уже загруженный снимок и досылает аудит после восстановления сервиса; при остановке одного из трёх JournalNode запись проходит, а при остановке двух запись не удаётся и активный NameNode останавливается, вместо того чтобы продолжать работу без большинства. Есть и вариант с Kerberos (только Hadoop 3.5.0): JournalNode тоже входят по keytab и открывают только HTTPS, ZKFC аутентифицируются в ZooKeeper через SASL, а к узлу выборов имеет доступ только principal NameNode (неаутентифицированному клиенту ZooKeeper запрещено даже чтение); тест проверяет, что после остановки активного NameNode другой узел принимает работу и продолжает применять политики к пользователям Kerberos, клиенты без билета всегда получают отказ, а после возвращения исходного узла можно переключиться обратно. JournalNode работают под principal с тем же коротким именем, что и NameNode, потому что JournalNode отдаёт edits только полному principal NameNode или запрашивающему с тем же коротким именем, что и у него самого, а HTTP-аутентификация даёт только короткое имя. Автоматический HA для Hadoop 2.7 не проверен: его клиент переключается, только если удаётся разрешить имена всех узлов NameNode, а имя остановленного в тесте контейнера больше не разрешается, поэтому клиент сразу сообщает UnknownHostException и не пробует другой узел. -Тест Kerberos выполняется на всех шести линиях (Java 17 и 21): KDC работает в JVM теста, NameNode и DataNode запускаются в защищённом режиме со своими keytab, DataNode передаёт данные только после SASL и работает только по HTTPS, а WebHDFS использует SPNEGO. Проверяется, что политики GrantForge применяются к чтению и записи после сопоставления principal с короткими именами, что пользователь без политики получает отказ, что отказы записываются в аудит под коротким именем, что клиент без билета получает отказ без отката к простой аутентификации и что перезапущенный NameNode снова входит в систему и продолжает применять политики. +Тест Kerberos выполняется на всех шести линиях версий (Java 17 и 21): KDC работает внутри тестовой JVM, NameNode и DataNode запускаются в защищённом режиме, каждый со своим keytab, передача данных DataNode требует SASL и открыт только HTTPS, а WebHDFS использует SPNEGO. Тест проверяет, что после сопоставления principal с коротким именем политики GrantForge действуют на чтение и запись, пользователи без политик получают отказ, события запрета аудируются под коротким именем, клиенты без билета получают отказ и не откатываются к аутентификации simple, а NameNode после перезапуска снова входит в систему и продолжает применять политики. -Общие нативные модульные тесты выполняются в `agents/grantforge-agent-hdfs-native/src/test`, а не компилируются во все шесть адаптеров. Тесты специфичных для версии callbacks остаются в соответствующих нумерованных модулях. Общие исходники контейнерных тестов в `agents/grantforge-agent-hdfs-common/src/test/shared` по-прежнему компилируются в нумерованные производственные модули. Отдельный Maven-проект для тестов не создаётся; Testcontainers остаётся test-зависимостью. Nightly проверяет все шесть версий Hadoop на тестовых хостах Java 17/21; JVM внутри контейнеров соответствует таблице выше. Отчёты и логи сохраняются. +Исходники тестов лежат в `src/test` настоящих рабочих модулей. Общие модульные тесты политик выполняются в common, общие нативные модульные тесты — в `agents/grantforge-agent-hdfs-native/src/test`, и в шесть версионных агентов они больше не компилируются. Тесты обратных вызовов каждой версии остаются в соответствующих нумерованных модулях, а общие исходники контейнерных тестов `agents/grantforge-agent-hdfs-common/src/test/shared` по-прежнему компилируются в каждый нумерованный модуль; отдельного тестового проекта Maven нет. Testcontainers подключён только в области test. Nightly проверяет шесть версий Hadoop на хостах с Java 17 и 21, а контейнеры Hadoop используют JVM из таблицы выше. Сейчас контейнеры покрывают аутентификацию Simple, ручной HA, Kerberos и HTTPS на всех шести линиях версий, автоматический HA для 2.10–3.5 и автоматический HA под Kerberos на Hadoop 3.5.0; автоматический HA для 2.7, автоматический HA под Kerberos на других версиях и патчи поставщиков по-прежнему нужно проверять в целевой среде. -Точки расширения Hadoop и семантику прав см. в [API Apache Hadoop 3.5.0](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/build/source/hadoop-hdfs-project/hadoop-hdfs/target/api/org/apache/hadoop/hdfs/server/namenode/INodeAttributeProvider.html) и [руководстве по правам HDFS](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/HdfsPermissionsGuide.html). +Точки расширения Hadoop и семантика прав описаны в [Apache Hadoop 3.5.0 API](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/build/source/hadoop-hdfs-project/hadoop-hdfs/target/api/org/apache/hadoop/hdfs/server/namenode/INodeAttributeProvider.html) и [руководстве по правам HDFS](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/HdfsPermissionsGuide.html). diff --git a/docs/content/ru/guide/catalog.md b/docs/content/ru/guide/catalog.md index 4a16f5a1..9ce34e21 100644 --- a/docs/content/ru/guide/catalog.md +++ b/docs/content/ru/guide/catalog.md @@ -22,6 +22,12 @@ description: Ведите приложения и деревья ресурсо - **OAuth-клиенты**: регистрация клиентов для бизнес-приложений, см. [OAuth 2.1 и OpenID Connect](/ru/integration/oauth/). - **Поля**: при выборе поля показывается, в каких API оно встречается (возвращается или принимается). +В каталоге приложения, где есть ресурсы, нажмите **Экспорт**, чтобы скачать JSON-файл для разработчиков и ИИ-инструментов. Файл содержит сведения о приложении, полную иерархию ресурсов и их зависимости вместе с кодами ресурсов на обоих концах зависимости; поиск и фильтры на содержимое экспорта не влияют. `formatVersion` указывает версию формата экспорта. + +Выберите целевое приложение, нажмите **Импорт**, загрузите файл экспорта версии 1 и сначала выполните предварительную проверку. Импорт повторно использует ресурсы с тем же кодом в том же приложении, только если совпадают тип и родитель, и не перезаписывает существующие настройки; конфликт типа, родителя или вида зависимости отменяет весь импорт. Новые ресурсы и зависимости записываются одним разом после подтверждения, и повторный импорт не создаёт дубликатов. Встроенные приложения импорт не поддерживают; объявленные зависимости после импорта становятся ручными. Для импорта ресурсов нужно право на создание ресурсов, для импорта зависимостей — ещё и право на их изменение. + +Экспорт содержит только сведения, зарегистрированные в GrantForge: `route` у страниц и подобных ресурсов — это маршрут фронтенда, а `code` у ресурсов API — код права. HTTP-методы и пути внешних бизнес-API, если они не зарегистрированы, не выводятся автоматически. JSON — это снимок на момент экспорта без чьих-либо результатов авторизации; после изменения ресурсов экспортируйте заново. + ## Каталог API **Управление платформой → Каталог API** перечисляет каждый API, зарегистрированный автоматически при запуске сервера, вместе с требованием к доступу: публичный, любой вошедший пользователь или конкретный код права. API, требующие авторизации, раскладываются по каталогу ресурсов по коду права, а выдачи ролей ссылаются на эти права. Если API добавлен, выведен из эксплуатации или изменился его код права, он попадает в список «ожидающих изменений»; каталог обновляется после подтверждения. diff --git a/docs/content/ru/guide/explain.md b/docs/content/ru/guide/explain.md index d79396dd..90424cca 100644 --- a/docs/content/ru/guide/explain.md +++ b/docs/content/ru/guide/explain.md @@ -13,6 +13,8 @@ description: Смотрите, что человек может делать и Нажмите «Показать эффективные права» на строке пользователя в разделе **Управление пользователями**, чтобы увидеть всё, что пользователь может использовать сейчас: роли, меню и кнопки, API, области данных и ограниченные поля. +Выберите приложение, чтобы увидеть его эффективные ресурсы и права API; «Почему» показывает источники — назначения ролей и прямые права пользователя. Роли, области данных и права на поля показываются для всей учётной записи. При моделировании добавления или удаления ролей существующие прямые права сохраняются. + ![Эффективные права](/screenshots/user-permissions.png) Нажмите **Объяснить** на любом элементе, и GrantForge ответит «почему это работает»: какая роль его предоставляет, через какие назначения (прямое, группа пользователей, подразделение, должность) и из каких выведенных ресурсов оно получено. Если что-то не работает, будет указано, не выдано ли это вовсе или же оно заблокировано правилом запрета. diff --git a/docs/content/ru/guide/identity-sources.md b/docs/content/ru/guide/identity-sources.md index fa60b730..c8a843d9 100644 --- a/docs/content/ru/guide/identity-sources.md +++ b/docs/content/ru/guide/identity-sources.md @@ -42,8 +42,13 @@ description: Позвольте пользователям входить чер | --- | --- | | Issuer URL | Адрес издателя (issuer) у провайдера; GrantForge обнаруживает конечные точки и ключи по его discovery-документу | | Client ID / secret | Клиент, зарегистрированный у провайдера; без секрета он работает как публичный клиент с использованием PKCE | -| Redirect URI | Значение `/api/v1/auth/federated/callback/<编码>`, показанное на странице; его нужно зарегистрировать у клиента провайдера | +| Redirect URI | Значение `/api/v1/auth/federated/callback/<код>`, показанное на странице; его нужно зарегистрировать у клиента провайдера | | Scopes и claims | По умолчанию `openid profile email`; имя пользователя, отображаемое имя и почта берутся соответственно из claims `preferred_username`, `name` и `email` | +| Claim телефона | По умолчанию читается `phone` (Casdoor); для других провайдеров можно указать `phone_number`. Чтобы синхронизировать номер телефона, scope должен включать `phone`. Если при входе провайдер не вернул номер, сохраняется прежний | +| Адрес аватара | Синхронизируется из стандартного claim OIDC `picture`; scope должен включать `profile`. Если значение не вернулось, сохраняется прежний адрес | +| Дополнительные параметры запроса авторизации | Необязательный JSON-объект, все значения которого — строки. Например, для Casdoor можно указать `{"provider_hint":"github"}`, чтобы выбрать настроенного провайдера. Параметры видны в адресной строке браузера, поэтому не указывайте в них секреты | + +Ключевые параметры протокола (включая `state`, `nonce`, `redirect_uri` и параметры PKCE) задаёт GrantForge; дополнительные параметры не могут их переопределить. После включения на странице входа появляется кнопка «Войти через <название>». Войдя у провайдера, пользователь возвращается в GrantForge; у учётных записей с включённой двухфакторной проверкой дополнительно потребуется ввести код подтверждения. @@ -54,3 +59,26 @@ description: Позвольте пользователям входить чер - Источник идентификации, который не создаёт учётные записи автоматически, пускает только уже связанных пользователей. - После отключения источника идентификации его пользователи не могут входить; источник идентификации, который ещё используется учётными записями, удалить нельзя. - С учётными записями источника идентификации можно обычным образом назначать роли и включать двухфакторную проверку. + +Синхронизация оргструктуры настраивается в отдельном источнике данных; менять настройки входа через OIDC не нужно. + +## Источники данных и синхронизация оргструктуры + +Источник данных отвечает за синхронизацию оргструктуры, а источник идентификации по-прежнему отвечает за вход через OIDC или LDAP. Они настраиваются независимо: отключение источника данных не влияет на вход через OIDC. + +1. Настройте существующий вход через OpenID Connect в разделе «Управление доступом → Источники идентификации». +2. В разделе «Управление доступом → Источники данных» добавьте источник DingTalk и выберите связанный источник идентификации OIDC. +3. Укажите CorpId, AppKey, AppSecret и ID корневого подразделения (обычно `1`). AppSecret шифруется отдельно, API никогда не возвращает его в открытом виде, а пустое поле при редактировании сохраняет прежнее значение. +4. Источник можно смонтировать под локальным подразделением. Интервал синхронизации по умолчанию — 30 минут, допускается 15–10080 минут; пустое значение означает только ручную синхронизацию. +5. После сохранения проверьте подключение и выполните первую синхронизацию. Приложению DingTalk нужны права на чтение подразделений и справочника сотрудников, и область доступа должна охватывать выбранное поддерево. +6. Когда пользователь входит через SSO, сотрудник находится по доверенной почте из OIDC, а подразделения и основное подразделение заполняются автоматически. Почту должен вести администратор, сотрудник не должен иметь возможности выдать себя за другого, и она должна совпадать с почтой в DingTalk. Если почты сотрудника нет, используется корпоративная почта. + +Вход читает только локальный снимок справочника и не обращается к DingTalk. При включённой MFA принадлежность обновляется после успешного второго шага. Учётные записи, вошедшие до первой синхронизации, временно сохраняют доверенную почту и связываются после её завершения; редактируемая почта локального профиля в сопоставлении не участвует. Отсутствующие, повторяющиеся или уже привязанные к другой учётной записи адреса не создают новых привязок. + +Привязка к сотруднику держится на внешнем ID сотрудника и не переходит к другому сотруднику при смене почты. Синхронизация учитывает принадлежность к нескольким подразделениям; основным остаётся прежнее основное подразделение, если оно действительно, иначе выбирается первое по стабильному порядку ID подразделений. Локальные подразделения, группы, должности и прямые назначения ролей сохраняются. Если назначить роль синхронизированному подразделению, пользователи наследуют её по обычным правилам, а перевод в другое подразделение обновляет права. + +Как только появились снимок оргструктуры или учётные записи, ожидающие связывания, связанный источник идентификации, организация, приложение, корневое подразделение и точка монтирования фиксируются. Синхронизированные подразделения и принадлежность доступны только для чтения и не переопределяются вручную в управлении пользователями или оргструктурой. Исчезнувшие подразделения сохраняются, чтобы не потерять выданные права; когда сотрудник выходит из разрешённого справочника, принадлежность, которой управляет этот источник, снимается, но учётная запись автоматически не отключается. Источник данных, которому принадлежат подразделения, нельзя удалить напрямую — его можно отключить. + +Синхронизация сначала получает все страницы и только затем атомарно применяет результат. При ошибке чтения, нарушении иерархии или изменении области доступа сохраняется последний успешный снимок; администратор проверяет результат по сводке синхронизации, списку проблем и журналу аудита. Если область доступа изменилась, сначала верните одобренную область, а затем запускайте синхронизацию. + +Сейчас используется [официальный серверный SDK DingTalk](https://open.dingtalk.com/document/development/download-the-server-side-sdk); собственных HTTP-вызовов нет. Адаптеры поставщиков выдают единые снимки подразделений и сотрудников, поэтому будущий адаптер Feishu сможет повторно использовать ту же логику синхронизации, привязки и обновления прав. diff --git a/docs/content/ru/guide/users.md b/docs/content/ru/guide/users.md index a66926e9..b1f1ba04 100644 --- a/docs/content/ru/guide/users.md +++ b/docs/content/ru/guide/users.md @@ -31,6 +31,7 @@ description: Создание, поиск, изменение, отключен | Сбросить двухфакторную проверку | Для утерянного аутентификатора: отключает двухфакторную проверку учётной записи и завершает её сессии | | Просмотреть роли | Роли, которыми обладает учётная запись, и источник каждой из них (напрямую, группа, подразделение, должность) | | Просмотреть эффективные права | См. [Объяснение, моделирование и аудит прав](/ru/guide/explain/) | +| Выдать права напрямую | Выбрать бизнес-приложение и разрешить пользователю отдельно меню, страницы, кнопки или API, при необходимости до заданного срока | | Удалить | Удаляет учётную запись и её членства в подразделениях; записи аудита сохраняются | Системную учётную запись (администратора, созданного при инициализации) и вашу собственную учётную запись нельзя отключить, заблокировать или удалить. @@ -38,3 +39,5 @@ description: Создание, поиск, изменение, отключен ## Самостоятельная регистрация По умолчанию отключена. После задания `grantforge.security.registration-enabled=true` на странице входа появляется пункт «Создать учётную запись». Зарегистрированные учётные записи попадают в тенант, указанный в `grantforge.security.registration-tenant`, как обычные учётные записи без ролей. + +Прямые права учитываются вместе с правами ролей: запрет в роли по-прежнему важнее, а зависимости ресурсов выводятся по обычным правилам. Разделение обязанностей на основе ролей на прямые права не распространяется; они не дают политик строк или полей и не применяются к приложению консоли. Выдавать их может только администратор с правом назначать роли, которому разрешено изменять этого пользователя. diff --git a/docs/content/ru/integration/java.md b/docs/content/ru/integration/java.md index 6da4a197..85669c3a 100644 --- a/docs/content/ru/integration/java.md +++ b/docs/content/ru/integration/java.md @@ -48,7 +48,7 @@ GrantForge требует PKCE для всех клиентов; включит ## Права API ```java -@RequirePermission("orders.delete") // 方法或类上;全部编码都需要 +@RequirePermission("orders.delete") // на методе или классе; нужны все коды @DeleteMapping("/api/orders/{id}") public void delete(@PathVariable long id) { ... } ``` @@ -63,16 +63,16 @@ grantForge.require("orders.read", "orders.export"); ## Права на данные -Объявите их на JPA-сущности; при старте стартовый пакет регистрирует сущность в GrantForge с помощью конфиденциального клиента, после чего в правах на данные роли можно настроить политики для `<应用编码>:order`: +Объявите их на JPA-сущности; при старте стартовый пакет регистрирует сущность в GrantForge с помощью конфиденциального клиента, после чего в правах на данные роли можно настроить политики для `<код приложения>:order`: ```java @Entity @GrantForgeEntity(code = "order", name = "Orders", owner = "ownerId", unit = "unitId", tenant = "tenantId") public class ShopOrder { - private long ownerId; // GrantForge 账号 ID,“本人”范围比较它 - private Long unitId; // GrantForge 部门 ID,部门范围比较它 - private String tenantId; // 可选:映射后,除“全部”外的规则都要求同租户 - @GrantForgeField("Status") private Status status; // 条件可测试的字段:文本、数字、布尔、枚举(选项)、时间 + private long ownerId; // ID учётной записи GrantForge, его сравнивает область «только свои» + private Long unitId; // ID подразделения GrantForge, его сравнивают области подразделений + private String tenantId; // необязательно: после сопоставления все правила, кроме «Все», требуют того же арендатора + @GrantForgeField("Status") private Status status; // поле, которое могут проверять условия: текст, число, логическое, перечисление (варианты), время @GrantForgeField("Total") private long total; } ``` @@ -81,7 +81,7 @@ public class ShopOrder { ```java orders.findAll(scopes.scope(ShopOrder.class, DataAction.READ).and(myFilters)); -orders.findOne(scopes.scope(ShopOrder.class, DataAction.DELETE).and(byId(id))); // 范围外的行视为不存在 +orders.findOne(scopes.scope(ShopOrder.class, DataAction.DELETE).and(byId(id))); // строки вне области считаются отсутствующими ``` Семантика совпадает с консолью GrantForge: строка доступна, когда действует правило «разрешить» и ни одно правило «запретить» не совпадает; если правил нет вовсе, ничего не доступно; «моё подразделение и ниже» разворачивается GrantForge в список идентификаторов подразделений. Правило «строка принадлежит подразделению владельца» пока не поддерживается. diff --git a/docs/content/ru/integration/javascript.md b/docs/content/ru/integration/javascript.md index fe2a5a9b..2d9d83fa 100644 --- a/docs/content/ru/integration/javascript.md +++ b/docs/content/ru/integration/javascript.md @@ -19,11 +19,11 @@ import { GrantForgeAuth, GrantForgeClient } from '@grantforge/client' const auth = new GrantForgeAuth({ issuer: 'https://grantforge.example.com', clientId: 'gf_xxx', redirectUri: `${location.origin}/` }) if (new URLSearchParams(location.search).has('code')) { - const { returnTo } = await auth.handleRedirect() // 换取令牌并校验 nonce + const { returnTo } = await auth.handleRedirect() // обменивает код на токены и проверяет nonce history.replaceState(null, '', returnTo ?? '/') } -if (!auth.signedIn) await auth.login(location.pathname) // 跳转 GrantForge 登录 -const token = await auth.accessToken() // 过期前自动续期;null 表示需要重新登录 +if (!auth.signedIn) await auth.login(location.pathname) // переход ко входу в GrantForge +const token = await auth.accessToken() // продлевается до истечения; null означает, что нужен повторный вход ``` Токены хранятся в sessionStorage и живут вместе со вкладкой. GrantForge не выдаёт refresh-токены публичным клиентам, поэтому после истечения access token пользователю приходится входить заново. @@ -46,9 +46,10 @@ app.use(createGrantForge(grantForge)) ``` ```vue - - - ``` + + + +``` `useGrantForge()` предоставляет `authorization`, `error`, `refresh()`, `can()` и `hasResource()`. Скрытие кнопок — лишь удобство; API приложения обязано проверять те же права. diff --git a/docs/content/ru/integration/open-api.md b/docs/content/ru/integration/open-api.md index 263551a3..3eee3c58 100644 --- a/docs/content/ru/integration/open-api.md +++ b/docs/content/ru/integration/open-api.md @@ -55,19 +55,19 @@ curl -H "Authorization: Bearer $TOKEN" https://grantforge.example.com/api/v1/ope "entities": [ { "code": "order", - "name": "订单", + "name": "Заказ", "owned": true, "unitBased": false, "fields": [ - { "code": "status", "name": "状态", "type": "CHOICE", "choices": ["NEW", "PAID", "SHIPPED"] }, - { "code": "total", "name": "金额", "type": "NUMBER" } + { "code": "status", "name": "Статус", "type": "CHOICE", "choices": ["NEW", "PAID", "SHIPPED"] }, + { "code": "total", "name": "Сумма", "type": "NUMBER" } ] } ] } ``` -После объявления сущности появляются в редакторе прав на данные роли как `<应用编码>:<实体编码>` (например, `shop:order`). `owned` означает, что у каждой строки есть учётная запись-владелец (включает область «только собственные записи»), а `unitBased` — что каждая строка относится к подразделению (включает области по подразделениям). Приложения на Java не обязаны писать этот запрос вручную: стартовый пакет объявляет сущности автоматически по аннотации `@GrantForgeEntity`. +После объявления сущности появляются в редакторе прав на данные роли как `<код приложения>:<код сущности>` (например, `shop:order`). `owned` означает, что у каждой строки есть учётная запись-владелец (включает область «только собственные записи»), а `unitBased` — что каждая строка относится к подразделению (включает области по подразделениям). Приложения на Java не обязаны писать этот запрос вручную: стартовый пакет объявляет сущности автоматически по аннотации `@GrantForgeEntity`. ## Ошибки diff --git a/docs/content/ru/integration/overview.md b/docs/content/ru/integration/overview.md index 71d6aa91..95f2091e 100644 --- a/docs/content/ru/integration/overview.md +++ b/docs/content/ru/integration/overview.md @@ -23,7 +23,7 @@ GrantForge выполняет для бизнес-приложений две р | Клиент | Каталог ресурсов → «OAuth-клиенты» приложения | Идентичность, которую приложение использует для входа пользователей и получения токенов. Браузерные приложения используют **публичные клиенты**, серверные — **конфиденциальные клиенты** | | scope | Настройки клиента | `openid`, `profile` и `email` для входа; `permissions` позволяет токену запрашивать права; `catalog` позволяет приложению объявлять сущности данных от своего имени | | Роли и выдачи | Управление доступом → Управление ролями | Выдавайте ресурсы приложения ролям, а затем назначайте роли пользователям, группам, подразделениям или должностям | -| Политики данных | Роль → Права на данные | Сущности данных, объявленные приложением (`<应用编码>:<实体>`), настраиваются так же, как собственные сущности консоли: все, всё в этом тенанте, только собственные записи, это подразделение, указанные подразделения или по условию | +| Политики данных | Роль → Права на данные | Сущности данных, объявленные приложением (`<код приложения>:<сущность>`), настраиваются так же, как собственные сущности консоли: все, всё в этом тенанте, только собственные записи, это подразделение, указанные подразделения или по условию | Администраторы тенантов (те, у кого есть системная роль) могут выдавать роли в собственном тенанте любой ресурс бизнес-приложения; права самой консоли по-прежнему можно выдавать только в пределах того, чем владеет тот, кто их выдаёт. @@ -31,19 +31,19 @@ GrantForge выполняет для бизнес-приложений две р ```mermaid sequenceDiagram - participant B as 浏览器 - participant A as 业务应用 + participant B as Браузер + participant A as Приложение participant G as GrantForge B->>G: /oauth2/authorize(PKCE) - G-->>B: 未登录时转到控制台登录页,登录后带回授权请求 - G-->>B: 回到应用的回调地址,带 code + G-->>B: Без входа: страница входа консоли, затем возврат к запросу авторизации + G-->>B: Возврат на адрес обратного вызова приложения с code B->>G: /oauth2/token(code + code_verifier) - G-->>B: 访问令牌、ID 令牌 + G-->>B: Токен доступа, ID-токен B->>G: /api/v1/open/me/authorization(Bearer) - G-->>B: 角色、资源、API 权限(ETag) - B->>A: 调用应用 API(Bearer) - A->>G: /api/v1/open/me/authorization、/data-access(同一个令牌) - A-->>B: 只返回用户可用的数据 + G-->>B: Роли, ресурсы, права API (ETag) + B->>A: Вызов API приложения (Bearer) + A->>G: /api/v1/open/me/authorization, /data-access (тот же токен) + A-->>B: Только данные, доступные пользователю ``` ## Шаги интеграции diff --git a/docs/content/ru/reference/errors.md b/docs/content/ru/reference/errors.md index f2e6b93e..14795641 100644 --- a/docs/content/ru/reference/errors.md +++ b/docs/content/ru/reference/errors.md @@ -16,7 +16,7 @@ description: Все стабильные коды ошибок, статусы H "type": "about:blank", "title": "Forbidden", "status": 403, - "detail": "你没有执行此操作的权限", + "detail": "У вас нет прав на выполнение этого действия.", "code": "GF-SECURITY-002", "requestId": "0f6c1a…" } diff --git a/docs/content/ru/start/concepts.md b/docs/content/ru/start/concepts.md index d9bac3f0..73750c59 100644 --- a/docs/content/ru/start/concepts.md +++ b/docs/content/ru/start/concepts.md @@ -13,18 +13,18 @@ description: Тенанты, учётные записи, ресурсы, рол ```mermaid flowchart LR - A[账号] -->|直接分配| R[角色] - G[用户组] -->|分配| R - O[部门] -->|分配,可含下级| R - P[岗位] -->|分配| R - A -.属于.-> G - A -.属于.-> O - A -.任职.-> P - R -->|继承| R2[父角色] - R -->|授权 允许/拒绝| S[资源] - S --- M[菜单 · 页面 · 按钮] + A[Учётная запись] -->|прямое назначение| R[Роль] + G[Группа] -->|назначение| R + O[Подразделение] -->|назначение, можно с дочерними| R + P[Должность] -->|назначение| R + A -. входит в .-> G + A -. входит в .-> O + A -. занимает .-> P + R -->|наследует| R2[Родительская роль] + R -->|разрешает/запрещает| S[Ресурс] + S --- M[Меню · Страница · Кнопка] S --- API[API] - S --- D[数据实体 · 字段] + S --- D[Сущность данных · Поле] ``` ## Тенанты diff --git a/docs/content/ru/start/quick-start.md b/docs/content/ru/start/quick-start.md index 8b00f6a2..9e8ed893 100644 --- a/docs/content/ru/start/quick-start.md +++ b/docs/content/ru/start/quick-start.md @@ -38,7 +38,7 @@ WARN ... First-run setup is pending. Open the console and enter this setup toke ## 2. Завершение инициализации -Откройте в браузере http://127.0.0.1:9999/, и консоль сразу откроет страницу настройки. Введите токен из лога, название организации, а также имя пользователя и пароль для первого администратора (не менее 12 символов). +Откройте в браузере , и консоль сразу откроет страницу настройки. Введите токен из лога, название организации, а также имя пользователя и пароль для первого администратора (не менее 12 символов). ![Первоначальная настройка и страница входа](/screenshots/login.png) diff --git a/docs/content/zh-tw/changelog/2026.4.0.md b/docs/content/zh-tw/changelog/2026.4.0.md new file mode 100644 index 00000000..fe06ffff --- /dev/null +++ b/docs/content/zh-tw/changelog/2026.4.0.md @@ -0,0 +1,68 @@ +--- +title: "2026.4.0" +description: "GrantForge 2026.4.0(2026-11-08):Elasticsearch、Apache Doris、MongoDB 與 Redis/Valkey 的服務外掛及代理,釘釘組織架構同步,使用者直接授權,資源目錄匯入匯出,以及檢查每種語言翻譯完整性的 CI。" +--- + + +2026.4.0 將外部系統權限擴充到 Elasticsearch、Apache Doris、MongoDB 以及 Redis 與 Valkey,四個代理都在系統旁執行,讓系統自身的存取控制與策略保持一致。身分與授權方面新增釘釘組織架構同步、使用者直接授權與資源目錄的匯入匯出。完整變更見 [GitHub 比較](https://github.com/devlive-community/grantforge/compare/v2026.3.0...v2026.4.0)。 + +## Elasticsearch + +- Elasticsearch 服務外掛:索引與資料串流、其欄位以及叢集;支援密碼、API 金鑰或 TLS 用戶端憑證登入;見 [Elasticsearch](/zh-tw/plugins/elasticsearch/)。 +- 在叢集旁執行的代理:為每位使用者維護 Elasticsearch 自己的角色,賦予原生使用者,並透過角色對應給其他 realm 的使用者;授權許可允許時帶有欄位層級與文件層級安全性;索引模式保持為模式,新索引立即可用;見 [Elasticsearch 代理](/zh-tw/external/elasticsearch-agent/)。 +- 存取記錄來自稽核日誌檔案。整合測試涵蓋 Elasticsearch 8.19、9.3、9.4 與 9.5。 + +## Apache Doris + +- Doris 服務外掛:Catalog、資料庫、資料表、欄位與工作負載群組;連線一個或多個 FE,以密碼登入;見 [Apache Doris](/zh-tw/plugins/doris/)。 +- 在叢集旁執行的代理:策略成為 Doris 自己的角色、欄位授權、工作負載群組授權與資料列策略,並授予使用者的每個身分;只有在 Doris 不會因此給出更多權限時,才授予整個資料庫或 Catalog;見 [Apache Doris 代理](/zh-tw/external/doris-agent/)。 +- 存取記錄來自 Doris 的稽核日誌表。整合測試涵蓋 Doris 2.1、3.1、4.0 與 4.1。 + +## MongoDB + +- MongoDB 服務外掛:資料庫與集合;連線副本集或分片叢集,支援 SCRAM、用戶端憑證、LDAP 或 Kerberos 登入;見 [MongoDB](/zh-tw/plugins/mongodb/)。 +- 在部署旁執行的代理:為每位使用者維護一個 MongoDB 角色,一步替換其權限;透過 MongoDB 提供的繼承權限,回報 GrantForge 之外的內建與自訂角色實際給了什麼;見 [MongoDB 代理](/zh-tw/external/mongodb-agent/)。 +- 存取記錄來自 Enterprise 或 Percona 的稽核日誌檔案,或來自 Profiler。整合測試涵蓋 MongoDB 6.0、7.0、8.0 與 9.0,Kerberos 在 Percona Server for MongoDB 上驗證。 + +## Redis 與 Valkey + +- Redis 服務外掛:鍵、頻道與節點;連線單機及其副本或叢集,以 ACL 使用者登入,可選 TLS;見 [Redis](/zh-tw/plugins/redis/)。 +- 在部署旁執行的代理:在每個節點上讓每位使用者的 ACL 選擇器與策略保持一致,並以一條命令整體替換;根權限仍由維運人員管理,代理以 `ACL DRYRUN` 詢問 Redis 這些權限是否允許了策略拒絕的存取;見 [Redis 代理](/zh-tw/external/redis-agent/)。 +- 拒絕記錄來自各節點的 ACL 日誌。整合測試涵蓋 Redis 7.2、7.4、8.2、8.10 與 Valkey 8.1、9.1,以及一個 Redis 8.10 叢集。 + +## 代理 + +- ClickHouse、PostgreSQL、OpenSearch 以及本版新增的系統旁代理共用新的 `grantforge-agent-sync` 核心:啟動器、設定、計時器、同步流程、稽核讀取位置與日誌檔案追蹤;各代理只保留其系統特有的部分,設定與日誌格式不變。 + +## 身分與組織 + +- 資料來源:組織架構同步獨立於身分來源設定,首個配接器為釘釘,使用其官方服務端 SDK;使用者透過 SSO 登入後依可信電子郵件自動關聯部門與主部門;其資料表在全部六種支援的資料庫上遷移,包括 SQL Server 與 Oracle;見 [身分來源(LDAP 與 OIDC)](/zh-tw/guide/identity-sources/)。 +- OpenID Connect 身分來源可以同步手機號碼與頭像,並可在授權請求中附加參數(例如 Casdoor 的 `provider_hint`),協定自身的參數無法被覆寫。 + +## 授權與資源目錄 + +- 使用者直接授權:為單一使用者允許業務應用程式的選單、頁面、按鈕或介面,可設定到期時間,與角色權限一起計算,角色中的拒絕仍然優先;見 [使用者](/zh-tw/guide/users/)。 +- 資源目錄可以把應用程式的資源樹匯出為 JSON,再匯入另一個應用程式:先預檢,衝突時整個匯入不生效;平台管理員可以清空符合條件的應用程式資源樹;見 [資源目錄與 API 目錄](/zh-tw/guide/catalog/)。 + +## 服務端 + +- 啟動時印出 GrantForge 的橫幅、版本與 Java 版本,不再是 Spring Boot 的橫幅。 + +## 文件與發布 + +- 外掛頁以總覽開始,依系統類型分組並可摺疊,列出每個系統的執行方式、資料列過濾與遮罩支援以及驗證過的版本;各 README 依同樣的順序列出元件。 +- CI 檢查每個頁面在每種語言中的翻譯是否完整:段落、標題、表格、程式碼區塊與連結與中文頁一致,不含其他語言的文字,也不照抄英文頁;11 個 README 依英文 README 做同樣的檢查。檢查發現的缺漏翻譯已全部補齊。 +- 所有 README 的版本徽章都由版本檢查維護。 +- 在 fork 上不再執行發布文件與發布版本的工作流程。 +- 服務端 `grantforge-server` 不再發布到 Maven 儲存庫:它包含全部外掛,超出了儲存庫的大小限制;服務端隨發行包與映像提供。 + +## 問題修正 + +- 在資源編輯框中改變資源類型時,清除不再適用的資源編碼。 +- JavaScript 接入指南的最後一個程式碼區塊不再吞掉後面的內文,Docker 與快速開始頁面中的主控台位址不再把後面的文字當作連結。 +- 相依套件升級到修正安全公告的版本:MariaDB Connector/J 3.5.11、OpenTelemetry 1.67.0、Guava 33.7.2、Snappy 1.1.10.9,以及主控台與 SDK 中的 vue-i18n、glob 與 minimatch。 diff --git a/docs/content/zh-tw/deploy/docker.md b/docs/content/zh-tw/deploy/docker.md index 4e17cff3..5d97d6ea 100644 --- a/docs/content/zh-tw/deploy/docker.md +++ b/docs/content/zh-tw/deploy/docker.md @@ -43,7 +43,7 @@ docker compose -f deploy/compose/postgres.yml up -d docker compose -f deploy/compose/postgres.yml logs grantforge | grep "setup token" ``` -然後開啟 http://127.0.0.1:9999/。範例使用的預設資料庫密碼只適合試用,正式使用前請透過 `GRANTFORGE_DB_PASSWORD` 修改。MySQL 範例需要先把 `mysql-connector-j-<版本>.jar` 放進 `deploy/compose/drivers/`。 +然後開啟 。範例使用的預設資料庫密碼只適合試用,正式使用前請透過 `GRANTFORGE_DB_PASSWORD` 修改。MySQL 範例需要先把 `mysql-connector-j-<版本>.jar` 放進 `deploy/compose/drivers/`。 ## Helm diff --git a/docs/content/zh-tw/develop/development.md b/docs/content/zh-tw/develop/development.md index e3a46e16..647111bd 100644 --- a/docs/content/zh-tw/develop/development.md +++ b/docs/content/zh-tw/develop/development.md @@ -76,7 +76,7 @@ bash script/release/tag.sh 2026.1.0 --next 2026.2.0 # 設定版本、打標 - `script/ci/release.sh` 建置發行包、僅含發行依賴的 CycloneDX SBOM 與 `SHA256SUMS`; - 多架構映像推送到 `ghcr.io/devlive-community/grantforge`; -- Maven 構件(含原始碼套件與 Javadoc)發布到 GitHub Packages;儲存庫設定了 `CENTRAL_USERNAME`、`CENTRAL_PASSWORD`(Central Portal 權杖)、`GPG_PRIVATE_KEY` 與 `GPG_PASSPHRASE` 時,簽章後發布到 Maven Central; +- Maven 構件(含原始碼套件與 Javadoc)發布到 GitHub Packages;儲存庫設定了 `CENTRAL_USERNAME`、`CENTRAL_PASSWORD`(Central Portal 權杖)、`GPG_PRIVATE_KEY` 與 `GPG_PASSPHRASE` 時,簽章後發布到 Maven Central;服務端 `grantforge-server` 打包了全部外掛,超出儲存庫的大小限制,不發布到任何 Maven 儲存庫,只隨發行包與映像提供; - 建立 GitHub Release,內文是上一個發布版本(`v*` 或 `1.0.6` 這樣的數字標籤)以來的全部提交,依新功能、問題修復、效能等分組,附提交連結。 候選版標記為預發布,不更新映像的 `latest`。本機啟用 `central` profile 預設不會發布(`central.skip=true`),僅有發布工作流顯式傳入 `-Dcentral.skip=false`。 @@ -100,3 +100,5 @@ pnpm build # 輸出到 docs/out ``` 頁面是 `docs/content/` 下的 Markdown,導覽在 `docs/lib/navigation.ts`。API 參考與錯誤碼在建置時從契約與原始碼產生。截圖由 `script/docs/screenshots.sh` 啟動真實服務、寫入範例資料後用 Playwright 產生。 + +`pnpm test` 同時核對翻譯:每個中文頁面在其他十種語言都要有譯文,段落、標題層級、清單、表格、程式碼區塊與連結都與中文頁一致;頁面中不能出現不屬於該語言的文字(包括繁體與日文中的簡體字),也不能照抄英文頁的句子。儲存庫根目錄的 11 個 README 以英文 README 為準做同樣的檢查,指向本站的連結必須是對應語言的現有頁面。 diff --git a/docs/content/zh-tw/external/hdfs-agent.md b/docs/content/zh-tw/external/hdfs-agent.md index d922af6a..d2e9e0ce 100644 --- a/docs/content/zh-tw/external/hdfs-agent.md +++ b/docs/content/zh-tw/external/hdfs-agent.md @@ -9,70 +9,68 @@ description: 以對應 Hadoop 版本的 NameNode 代理執行 GrantForge 路徑 project root for full license text. --> -服務端外掛 `grantforge-plugin-hdfs` 定義資源與連線。Hadoop 2.7、2.10、3.2、3.3、3.4、3.5 的編號 NameNode 代理透過各自的 SPI 檢查存取,共用簽章快照、策略求值與稽核上報邏輯。 +服務端的 `grantforge-plugin-hdfs` 統一提供 `hdfs` 服務類型、資源與連線設定。NameNode 代理依 Hadoop 版本分別建置,透過對應版本的 `INodeAttributeProvider` 與 `AccessControlEnforcer` 檢查存取,共用簽章策略、快取、策略求值與稽核邏輯。 -共用 HDFS 邏輯位於 `agents/grantforge-agent-hdfs-common`,版本調適器位於 `agents/grantforge-agent-hdfs-`。共用原生調適位於正式 Maven 生產模組 `agents/grantforge-agent-hdfs-native`,採用 Java 8 / Hadoop 2.7.7 基準;各編號模組以 Maven 二進位依賴引用它,保留各版入口與特有回呼,不再重複編譯共用生產原始碼。`core/grantforge-agent-core` 保留為共用協定與執行基礎設施。 +原始碼中的服務端外掛位於 `plugins/grantforge-plugin-hdfs`,版本代理位於 `agents/grantforge-agent-hdfs-`,不依賴 Hadoop 的共用生產邏輯位於 `agents/grantforge-agent-hdfs-common`。共用原生調適位於正式 Maven 生產模組 `agents/grantforge-agent-hdfs-native`,採用 Java 8 / Hadoop 2.7.7 基線;各編號模組透過 Maven 二進位相依引用它,保留各版入口與特有回呼,不再重複編譯共用生產原始碼。協定、快照快取與稽核基礎設施仍位於 `core/grantforge-agent-core`。 -服務端外掛程式維持單一 `hdfs` 服務類型,用戶端版本與代理版本獨立。Hadoop 2.x 的連線與路徑查詢使用 `webhdfs://namenode:50070`,HTTPS 使用對應的 `swebhdfs://` 位址;Hadoop 3.x 可使用 RPC `hdfs://` 或 WebHDFS。容器測試驗證 2.x 的 WebHDFS,以及 3.x 的兩種協定。2.x RPC 尚未認證,外掛程式不會自動切換設定的連線協定。 +服務端外掛維持一個 `hdfs` 類型,用戶端版本獨立於代理版本。Hadoop 2.x 的連線與路徑查詢使用 `webhdfs://namenode:50070`(啟用 HTTPS 時使用對應的 `swebhdfs://` 位址);Hadoop 3.x 可使用 RPC `hdfs://` 或 WebHDFS。容器矩陣驗證 2.x 的 WebHDFS,以及 3.x 的 RPC 與 WebHDFS;未認證 2.x RPC,不會自動切換連線協定。 -Apache Hadoop 2.7.7 啟用 inode 屬性擴充後,一般使用者直接查詢根路徑 `/` 會在原生程式碼中觸發空指標,發生於代理回呼之前。此版本的資料操作與服務的 `lookup.path` 應使用實際資料目錄,例如 `/data`;容器測試保留根路徑失敗的獨立斷言。 +Apache Hadoop 2.7.7 在啟用 inode 屬性擴充後,一般使用者直接查詢根路徑 `/` 會在原生程式碼中觸發空指標,發生在代理回呼之前。此版本的資料操作與服務的 `lookup.path` 應使用實際資料目錄,例如 `/data`;測試保留了根路徑失敗的獨立斷言。 -## HDFS 代理目標版本 +## 選擇版本 -| Hadoop 基線 | 容器 Java | 代理目錄 | -| --- | --- | --- | -| 2.7.7 | Java 8 | `agents/hdfs/2.7/` | -| 2.10.2 | Java 8 | `agents/hdfs/2.10/` | -| 3.2.4 | Java 8 | `agents/hdfs/3.2/` | -| 3.3.6 | Java 8(amd64 映像) | `agents/hdfs/3.3/` | -| 3.4.3 | Java 11 | `agents/hdfs/3.4/` | -| 3.5.0 | Java 17 | `agents/hdfs/3.5/` | +| 代理模組後綴 | 驗證的 Apache Hadoop | 容器 JVM | 授權介面 | 超級使用者路徑回呼 | +| --- | --- | --- | --- | --- | +| `2.7` | 2.7.7 | Java 8 | 參數式 | 無 | +| `2.10` | 2.10.2 | Java 8 | 參數式 | 無 | +| `3.2` | 3.2.4 | Java 8 | 參數式 | 無 | +| `3.3` | 3.3.6 | Java 8 | 上下文式 | 無 | +| `3.4` | 3.4.3 | Java 11 | 上下文式、超級使用者及拒絕回呼 | 有 | +| `3.5` | 3.5.0 | Java 17 | 上下文式、超級使用者及拒絕回呼 | 有 | -依叢集的 Hadoop 系列,從 `agents/hdfs//` 選擇 `grantforge-agent-hdfs--.jar`。共用邏輯使用 Java 8 位元組碼,3.5 調適器使用 Java 17 位元組碼。 +例如 Hadoop 2.10.2 安裝 `agents/hdfs/2.10/grantforge-agent-hdfs-2.10-.jar`。每台 NameNode 只安裝一個版本的代理,升級時移除舊 jar,設定中的 provider 類別名稱保持不變。代理啟動時核對 Hadoop 主次版本及必要回呼,裝錯版本會停止啟動;同一主次版本的其他修補版本及廠商分支仍需另行驗證。 -Hadoop 2.7、2.10、3.2、3.3 不提供代理使用的超級使用者授權回呼,這些超級使用者存取仍由 Hadoop 管理。需要 GrantForge 策略約束的資料存取應使用一般使用者。 +`common` 與 `2.7` 至 `3.4` 的代理主程式碼使用 Java 8 位元組碼,`3.5` 使用 Java 17。上表列出實際整合測試的 JVM,不代表 3.4.3 已驗證 Java 8。3.3.6 官方測試映像僅提供 amd64,在 ARM 主機上明確以 amd64 模擬執行。 ## 權限關係 -以下執行語意描述一般使用者存取;超級使用者限制請參考上方與回呼說明。 - -代理先執行 HDFS 原生權限檢查,再執行 GrantForge 策略:使用者需要同時滿足原生權限與策略要求。GrantForge 的允許策略不會繞過 POSIX 權限、ACL、擁有者檢查或 sticky bit;拒絕策略始終拒絕。原生權限設定仍透過 Hadoop 的管理工具維護。 +代理先執行 HDFS 原生權限檢查,再執行 GrantForge 策略:使用者必須同時滿足原生權限與策略要求。GrantForge 的允許策略不會繞過 POSIX 權限、ACL、擁有者檢查或 sticky bit;拒絕策略一律拒絕。原生權限設定仍透過 Hadoop 的管理工具維護。 -預設 `grantforge.hdfs.native.fallback=false`:沒有本機策略快照、沒有相符策略或代理尚未啟動時拒絕資料存取。設為 `true` 後,未被策略決定的存取使用原生權限;明確拒絕策略仍然有效。服務端暫時不可連線時繼續使用最後一份通過簽章驗證的本機快照。 +預設 `grantforge.hdfs.native.fallback=false`:沒有本機策略快照、沒有符合的策略或代理尚未啟動時拒絕資料存取。設為 `true` 後,未被策略決定的存取使用原生權限;明確的拒絕策略仍然有效。服務端暫時無法連線時,繼續使用最後一份通過簽章驗證的本機快照。 一次授權回呼中的祖先、目標、子樹與快照路徑投影使用同一版策略快照;重新整理後的策略在下一次回呼生效,避免組合不同版本的允許規則。存取稽核記錄實際使用的策略版本。 代理檢查一般使用者存取目標所需的 `read`、`write`、`execute`,也檢查父目錄、祖先目錄與需要遞迴驗證的子目錄。建立、刪除、重新命名等操作涉及多個路徑,允許策略必須涵蓋它們。嚴格模式下,只有目標檔案的 `read` 策略還不夠,需要為使用者設定祖先目錄的 `execute` 策略,例如允許 `/` 上的 `execute` 並勾選遞迴,再為實際資料目錄設定讀寫權限。 -快照路徑同時檢查實際請求路徑與去掉 `.snapshot/<快照名>` 後的原本路徑,例如 `/data/.snapshot/s1/secret` 同時檢查 `/data/secret`。原本路徑上的拒絕策略因此也約束快照;可以再為明確快照路徑設定更嚴格的限制。中介資料查詢沿用 HDFS 的目錄走訪權限語意。 +快照路徑同時檢查實際請求路徑與去掉 `.snapshot/<快照名稱>` 後的原路徑,例如 `/data/.snapshot/s1/secret` 同時檢查 `/data/secret`。原路徑上的拒絕策略因此也約束快照;可以再為明確的快照路徑設定更嚴格的限制。中繼資料查詢沿用 HDFS 的目錄走訪權限語意。 -一次遞迴授權最多檢查 `100000` 個 inode,超過上限會拒絕操作,避免在 NameNode 內無限配置記憶體。超長路徑使用完整路徑判定策略;稽核資源展示限制為 `1000` 字元,並在請求詳情中記錄原本長度與 SHA-256 摘要。 +一次遞迴授權最多檢查 `100000` 個 inode,超過上限會拒絕操作,避免在 NameNode 內無限配置記憶體。超長路徑使用完整路徑判定策略;稽核資源的顯示限制為 `1000` 字元,並在請求詳情中記錄原長度與 SHA-256 摘要。 -以下超級使用者回呼說明適用於 Hadoop 3.4 與 3.5。HDFS 超級使用者仍由 Hadoop 管理。帶路徑的超級使用者回呼先透過 Hadoop 的超級使用者檢查,再依 Hadoop 3.4 / 3.5 提供的操作名稱檢查策略:檔案讀取與中介資料查詢要求 `read`,目錄列舉要求 `read` + `execute`,已知修改操作要求 `write`。未知、缺少或無法準確推斷的操作(例如 `checkAccess` 和 `concat`)保守地要求三種權限。 +Hadoop 2.7、2.10、3.2、3.3 在呼叫代理前略過超級使用者,代理無法管控或稽核這些存取。Hadoop 3.4、3.5 的帶路徑超級使用者回呼先通過原生檢查,再依操作名稱檢查策略:檔案讀取與中繼資料查詢要求 `read`,目錄列舉要求 `read` + `execute`,已知的修改操作要求 `write`。未知、缺失或無法準確推斷的操作(例如 `checkAccess` 與 `concat`)保守地要求三種權限。 -超級使用者回呼沒有完整 inode 與子樹上下文,無路徑的叢集管理呼叫保留原生檢查;無法用子目錄策略限制超級使用者的所有遞迴操作。資料使用者應使用一般 Hadoop 使用者。 +超級使用者回呼沒有完整的 inode 與子樹上下文,無路徑的叢集管理呼叫保留原生檢查;無法以子目錄策略限制超級使用者的所有遞迴操作。資料使用者應使用一般 Hadoop 使用者。 ## 指標 -代理透過 Hadoop 的 Metrics2 體系回報指標,與 NameNode 自身的 dfs 指標使用同一套 sink,在 NameNode 的 JMX 裡是 `Hadoop:service=NameNode,name=GrantForgeHdfsAgent`(Prometheus 的 JMX 匯出器可以直接抓取)。每個指標帶有 `instance`(資料服務執行個體名)與 `agentVersion` 標籤,HA 的兩個 NameNode 因此可以分別查看。指標註冊失敗只損失指標本身:代理會記錄警告並在沒有指標的情況下繼續執行授權。 +代理透過 Hadoop 的 Metrics2 體系回報指標,與 NameNode 自身的 dfs 指標使用同一套 sink,在 NameNode 的 JMX 中是 `Hadoop:service=NameNode,name=GrantForgeHdfsAgent`(Prometheus 的 JMX 匯出器可以直接擷取)。每個指標帶有 `instance`(資料服務執行個體名稱)與 `agentVersion` 標籤,因此 HA 的兩個 NameNode 可以分別檢視。指標註冊失敗只損失指標本身:代理會記錄警告,並在沒有指標的情況下繼續執行授權。 | 指標 | 說明 | | --- | --- | | `Callbacks` | 代理執行的授權回呼數 | -| `SuperuserCallbacks` | 代理執行的超級使用者回呼數 (3.4 / 3.5) | -| `NativeDenies` | Hadoop 在代理之前就拒絕的存取次數 | -| `EvaluationFailures` | 因策略求值例外而失敗關閉的回呼次數 | -| `DecisionsAllowed` / `DecisionsDenied` / `DecisionsUndetermined` | 策略判定為允許 / 拒絕 / 未定的權限次數;未定在嚴格模式下同樣拒絕 | -| `MissingSnapshots` | 沒有已驗證策略快照時服務的回呼次數 | +| `SuperuserCallbacks` | 代理執行的超級使用者回呼數 | +| `NativeDenies` | Hadoop 在代理之前就拒絕的存取數 | +| `EvaluationFailures` | 因策略求值例外而失敗關閉的回呼數 | +| `DecisionsAllowed` / `DecisionsDenied` / `DecisionsUndetermined` | 策略判定為允許 / 拒絕 / 未決的權限數;未決在嚴格模式下同樣拒絕 | +| `MissingSnapshots` | 沒有已驗證策略快照時服務的回呼數 | | `SnapshotVersion` | 目前使用的策略快照版本,0 表示沒有 | -| `QueuedEvents` / `DroppedEvents` | 記憶體中待回報的稽核事件數 / 因佇列或磁碟緩衝滿而丟棄的事件數 | -| `ServerReachable` | 最後一次連線策略伺服器是否成功(1/0) | +| `QueuedEvents` / `DroppedEvents` | 記憶體中待回報的稽核事件數 / 因佇列或磁碟緩衝已滿而捨棄的事件數 | +| `ServerReachable` | 最後一次存取策略伺服器是否成功(1/0) | ## 部署 -1. 在 GrantForge 的資料服務中新增 `hdfs` 服務,儲存設定並測試連線;為實際 Hadoop 短使用者名稱、使用者群組或角色設定路徑策略。 -2. 在「資料權限 → 代理」中為這個服務簽發權杖。將權杖原文寫到每個 NameNode 的本機檔案,例如 `/etc/hadoop/grantforge/token`,由 NameNode 執行使用者讀取。 -3. 依叢集的 Hadoop 系列,將 `agents/hdfs//` 的 `grantforge-agent-hdfs--.jar` 複製到 NameNode 類別路徑,例如 `$HADOOP_HOME/share/hadoop/hdfs/lib/`。只安裝一個對應調適器。jar 內含共用邏輯、Jackson 與簽章函式庫,Hadoop 類別由 NameNode 提供。 +1. 在 GrantForge 的資料服務中新增 `hdfs` 服務,儲存設定並測試連線;為實際的 Hadoop 短使用者名稱、使用者群組或角色設定路徑策略。 +2. 在「資料權限 → 代理」中為此服務簽發權杖。將權杖原文寫到每個 NameNode 的本機檔案,例如 `/etc/hadoop/grantforge/token`,由 NameNode 執行使用者讀取。 +3. 依上表從發行包 `agents/hdfs//` 選擇對應的代理 jar,放入 NameNode 的類別路徑,例如 `$HADOOP_HOME/share/hadoop/hdfs/lib/`。代理 jar 已包含自己的策略引擎、Jackson 與簽章函式庫,Hadoop 類別由 NameNode 提供;心跳中的代理版本包含產品版本與編譯所用的 Hadoop 版本。 4. 在每個 NameNode 的 `hdfs-site.xml` 中設定以下屬性;HA 的兩個 NameNode 使用不同的 `instance` 與各自本機的快取目錄。 ```xml @@ -102,9 +100,9 @@ Hadoop 2.7、2.10、3.2、3.3 不提供代理使用的超級使用者授權回 ``` -5. 確認 `dfs.permissions.enabled=true`,且 `dfs.namenode.inode.attributes.provider.bypass.users` 為空;代理啟動時會拒絕可繞過授權回呼的設定。重新啟動 NameNode,然後檢查 GrantForge 代理頁面中的心跳與策略版本。代理讀取 NameNode 現有設定;不修改 inode 的原生屬性。 +5. 確認 `dfs.permissions.enabled=true`,且 `dfs.namenode.inode.attributes.provider.bypass.users` 為空;代理啟動時會拒絕可繞過授權回呼的設定。重新啟動 NameNode,然後在 GrantForge 代理頁面檢查心跳與策略版本。代理讀取 NameNode 既有設定;不修改 inode 的原生屬性。 -首次部署可先使用 `native.fallback=true`,確認策略快照已同步並補齊祖先目錄權限,再切換嚴格模式。權杖綁定的服務類型必須是 `hdfs`;設定錯誤或綁定到其他服務類型會拒絕存取。 +首次部署可先使用 `native.fallback=true`,確認策略快照已同步並補齊祖先目錄權限,再切換到嚴格模式。權杖綁定的服務類型必須是 `hdfs`;設定錯誤或綁定到其他服務類型會拒絕存取。 ## 選用設定 @@ -112,48 +110,46 @@ Hadoop 2.7、2.10、3.2、3.3 不提供代理使用的超級使用者授權回 | --- | --- | --- | | `grantforge.hdfs.connect.timeout.ms` | `5000` | 連線 GrantForge 的逾時 | | `grantforge.hdfs.read.timeout.ms` | `8000` | 讀取回應的逾時 | -| `grantforge.hdfs.refresh.interval.ms` | `30000` | 伺服器不可連線時的策略重新整理間隔,至少 `1000`;正常心跳使用服務端建議間隔 | -| `grantforge.hdfs.signing.key.file` | 未設定 | 選用的簽章公鑰檔案,內容為主控台提供的 Base64 X.509 公鑰;設定後只接受該公鑰的簽章 | -| `grantforge.hdfs.audit.batch.size` | `500` | 每次回報最多事件數,範圍 `1..1000` | -| `grantforge.hdfs.audit.queue.capacity` | `10000` | 記憶體稽核佇列容量,範圍 `1..1000000`,至少能容納一個批次;佇列滿時計數並丟棄新事件 | -| `grantforge.hdfs.audit.flush.interval.ms` | `5000` | 稽核重新整理間隔,正整數,最大 `2147483647`;調小可降低回報延遲 | -| `grantforge.hdfs.audit.spool.limit.bytes` | `67108864` | 伺服器不可連線時磁碟緩衝上限,非負整數;`0` 停用磁碟緩衝 | +| `grantforge.hdfs.refresh.interval.ms` | `30000` | 伺服器無法連線時的策略重新整理間隔,至少 `1000`;正常心跳使用服務端建議的間隔 | +| `grantforge.hdfs.signing.key.file` | 未設定 | 選用的簽章公鑰檔案,內容為主控台提供的 Base64 X.509 公鑰;設定後只接受此公鑰的簽章 | +| `grantforge.hdfs.audit.batch.size` | `500` | 每次回報的最多事件數,範圍 `1..1000` | +| `grantforge.hdfs.audit.queue.capacity` | `10000` | 記憶體稽核佇列容量,範圍 `1..1000000`,至少能容納一個批次;佇列滿時計數並捨棄新事件 | +| `grantforge.hdfs.audit.flush.interval.ms` | `5000` | 稽核排清間隔,正整數,最大 `2147483647`;調小可降低回報延遲 | +| `grantforge.hdfs.audit.spool.limit.bytes` | `67108864` | 伺服器無法連線時的磁碟緩衝上限,非負整數;`0` 停用磁碟緩衝 | -突發存取量較大時可增大稽核佇列,減少佇列溢位;縮短重新整理間隔可降低稽核延遲,也會增加回報頻率。磁碟緩衝限制用於控制長時間斷線的磁碟佔用,停用或耗盡緩衝時事件可能遺失。稽核回報在背景執行,不等待策略伺服器回應。 +突發存取量較大時可加大稽核佇列,減少佇列溢位;縮短排清間隔可降低稽核延遲,也會增加回報頻率。磁碟緩衝限制用於控制長時間斷線時的磁碟用量,停用或耗盡緩衝時事件可能遺失。稽核回報在背景執行,不等待策略伺服器回應。 -未設定簽章公鑰時,代理首次從伺服器取得公鑰並隨快照儲存。代理使用 Hadoop 傳入的短使用者名稱與使用者群組,角色與額外群組來自簽章快照;Kerberos principal 的短名對應由叢集的 `hadoop.security.auth_to_local` 決定。 +未設定簽章公鑰時,代理首次從伺服器取得公鑰並隨快照儲存。代理使用 Hadoop 傳入的短使用者名稱與使用者群組,角色及額外群組來自簽章快照;Kerberos principal 的短名稱對應由叢集的 `hadoop.security.auth_to_local` 決定。 ## 從原始碼建置與驗證 -以下命令以 3.5 為例;可依目標調適器替換成 2.7、2.10、3.2、3.3 或 3.4。 - ```sh ./mvnw -pl agents/grantforge-agent-hdfs-3.5 -am package ./mvnw -pl plugins/grantforge-plugin-hdfs,agents/grantforge-agent-hdfs-3.5,core/grantforge-plugin-host -am test ``` -代理產物位於 `agents/grantforge-agent-hdfs-3.5/target/grantforge-agent-hdfs-3.5-.jar`。單元測試涵蓋 NameNode 授權回呼、設定、版本中介資料與策略決策;WebHDFS 與 Kerberos 測試啟動本機暫存服務。上線前還需在目標叢集驗證讀寫、建立、重新命名、遞迴刪除、HA 切換與斷線後的快取行為。 +代理產物位於 `agents/grantforge-agent-hdfs-3.5/target/grantforge-agent-hdfs-3.5-.jar`。單元測試涵蓋 NameNode 授權回呼、設定、版本中繼資料與策略決策;WebHDFS 與 Kerberos 測試啟動本機暫時服務。上線前還需要在目標叢集驗證讀寫、建立、重新命名、遞迴刪除、HA 切換與斷線後的快取行為。 整合驗證隨 `verify` 階段以 Testcontainers 執行(單元測試不啟動叢集;`verify` 需要可用的 Docker daemon): ```sh ./mvnw -pl agents/grantforge-agent-hdfs-3.5 -am verify # 與 nightly 使用同一入口 -bash script/ci/hdfs_integration.sh 3.5 -# 單一系列或完整矩陣 -bash script/ci/hdfs_integration.sh all +bash script/ci/hdfs_integration.sh +# 只驗證指定的版本線 +bash script/ci/hdfs_integration.sh 2.10 ``` -測試使用固定版本的 Apache Hadoop 容器映像,將真實打包的代理 jar 放入 NameNode 類別路徑。Testcontainers 建立隔離網路並管理 NameNode、DataNode 的生命週期,驗證讀寫、建立、附加、重新命名、刪除、遞迴與快照拒絕、原生權限及稽核、策略重新整理、中斷策略伺服器後重新啟動 NameNode 使用簽章快取,以及無快照時的嚴格模式與原生權限回退。 +測試使用固定版本的 Apache Hadoop 映像;舊版映像以固定摘要的 Java 8 基礎映像與經 SHA-512 驗證的 Apache 發行包建置。將對應版本的實際代理 jar 放入 NameNode 類別路徑,並斷言實際的 Hadoop 與 JVM 版本。Testcontainers 建立隔離網路並管理 NameNode、DataNode 的生命週期,驗證讀寫、建立、附加、重新命名、刪除、遞迴與快照拒絕、原生權限及稽核、策略重新整理、與策略伺服器斷線後重新啟動 NameNode 使用簽章快取,以及沒有快照時的嚴格模式與原生權限回退。 -需要執行中的 Docker daemon,並允許下載測試映像。Docker 不可用時測試失敗,不會靜默跳過。檔案系統客戶端在 Hadoop 容器內部執行,策略 HTTP 服務使用 Testcontainers 的主機連接埠轉送;無需外部 Hadoop 叢集。測試結束後清理容器與測試網路,日誌儲存到 `agents/grantforge-agent-hdfs-3.5/target/hdfs-testcontainers`。 +需要執行中的 Docker daemon,並允許下載測試映像。Docker 無法使用時測試失敗,不會靜默略過。檔案系統用戶端在 Hadoop 容器內部執行,策略 HTTP 服務使用 Testcontainers 的主機連接埠轉送;不需要外部 Hadoop 叢集。測試結束後清理容器與測試網路,日誌儲存到 `agents/grantforge-agent-hdfs-3.5/target/hdfs-testcontainers`。 -HA 測試啟動兩個 NameNode、一個 DataNode 與一個 JournalNode,為兩個代理設定獨立執行個體名與快取目錄。它使用邏輯 HDFS 客戶端手動切換活動節點,並驗證切換後的讀寫與拒絕策略;單一 JournalNode 僅用於測試,不驗證多數決容錯,也不涉及 ZooKeeper 自動故障轉移。 +HA 測試啟動兩個 NameNode、一個 DataNode 與一個 JournalNode,為兩個代理設定獨立的執行個體名稱與快取目錄。它使用邏輯 HDFS 用戶端手動切換作用中節點,並驗證切換後的讀寫與拒絕策略;單一 JournalNode 僅用於測試,不驗證多數派容錯,也不涉及 ZooKeeper 自動容錯移轉。 -自動 HA 測試(Hadoop 2.10 至 3.5,Java 17 與 21)啟動 ZooKeeper、三個 JournalNode、兩個各帶 ZKFC 的 NameNode 與一個 DataNode。測試驗證:直接停止作用中的 NameNode 後由 ZooKeeper 選出另一個節點接手,新的作用中節點繼續執行策略,拒絕事件歸屬於該節點的實例;節點離開期間發布的新策略在其以 Standby 恢復後生效,切回後仍執行新版本而不會退回舊版本;策略服務無法使用時,接手的節點以已載入的快照繼續執行,服務恢復後補送稽核;三個 JournalNode 停一個時寫入仍成功,停兩個時寫入失敗且作用中的 NameNode 停止,而非在沒有多數決的情況下繼續。另有一個 Kerberos 版本(僅 Hadoop 3.5.0):JournalNode 也以 keytab 登入並只開放 HTTPS,ZKFC 透過 SASL 向 ZooKeeper 認證,選舉節點只允許 NameNode 的 principal 存取(未認證的 ZooKeeper 用戶端連讀取都會被拒);測試驗證作用中的 NameNode 被停止後另一個節點接手,並繼續對 Kerberos 使用者執行策略,沒有票證的用戶端始終被拒,原節點恢復後可切回。JournalNode 以與 NameNode 相同短名的 principal 執行,因為 JournalNode 只把 edits 交給 NameNode 的完整 principal 或與自己短名相同的請求者,而 HTTP 認證只提供短名。Hadoop 2.7 的自動 HA 未驗證:它的用戶端只有在每個 NameNode 主機名都能解析時才會切換,測試中被停止的容器名稱不再可解析,用戶端會直接回報 UnknownHostException 而不嘗試另一個節點。 +自動 HA 測試(Hadoop 2.10 至 3.5,Java 17 與 21)啟動 ZooKeeper、三個 JournalNode、兩個各帶 ZKFC 的 NameNode 與一個 DataNode。測試驗證:直接停止作用中 NameNode 後由 ZooKeeper 選出另一個節點接管,新的作用中節點繼續執行策略,拒絕事件歸屬於該節點的執行個體;節點離開期間發布的新策略在其以 Standby 恢復後生效,切回後仍執行新版本而不會回到舊版本;策略服務無法使用時,接管的節點以已載入的快照繼續執行,服務恢復後補送稽核;三個 JournalNode 停一個時寫入仍成功,停兩個時寫入失敗且作用中 NameNode 停止,而不是在沒有多數派的情況下繼續。另有一個 Kerberos 版本(僅 Hadoop 3.5.0):JournalNode 也以 keytab 登入並只開放 HTTPS,ZKFC 透過 SASL 向 ZooKeeper 認證,選舉節點只允許 NameNode 的 principal 存取(未認證的 ZooKeeper 用戶端連讀取都會被拒);測試驗證作用中 NameNode 被停止後另一個節點接管,並繼續對 Kerberos 使用者執行策略,沒有票證的用戶端一律被拒,原節點恢復後可以切回。JournalNode 以與 NameNode 相同短名稱的 principal 執行,因為 JournalNode 只把 edits 交給 NameNode 的完整 principal 或與自己短名稱相同的請求者,而 HTTP 認證只提供短名稱。Hadoop 2.7 的自動 HA 未驗證:它的用戶端只有在每個 NameNode 主機名稱都能解析時才會切換,測試中被停止的容器名稱不再能解析,用戶端會直接回報 UnknownHostException,而不嘗試另一個節點。 -Kerberos 測試在全部六個版本線上執行(Java 17 與 21):KDC 執行於測試 JVM 中,NameNode 與 DataNode 以安全模式啟動,各自使用 keytab,DataNode 的資料傳輸需要 SASL 且只開放 HTTPS,WebHDFS 使用 SPNEGO。測試驗證 principal 對應為短名後 GrantForge 策略對讀寫生效、沒有策略的使用者被拒絕、拒絕事件以短名稽核、沒有票證的用戶端被拒絕且不會退回 simple 認證、NameNode 重新啟動後重新登入並繼續執行策略。 +Kerberos 測試在全部六個版本線上執行(Java 17 與 21):KDC 在測試 JVM 中執行,NameNode 與 DataNode 以安全模式啟動並各自使用 keytab,DataNode 的資料傳輸需要 SASL 且只開放 HTTPS,WebHDFS 使用 SPNEGO。測試驗證 principal 對應為短名稱後 GrantForge 策略對讀寫生效、沒有策略的使用者被拒、拒絕事件以短名稱稽核、沒有票證的用戶端被拒且不會回退到 simple 認證、NameNode 重新啟動後重新登入並繼續執行策略。 -共用原生單元測試在 `agents/grantforge-agent-hdfs-native/src/test` 執行,不再編譯進六個版本調適器。各版回呼測試仍留在對應的編號模組;共用容器測試原始碼仍位於 `agents/grantforge-agent-hdfs-common/src/test/shared`,編譯進各編號生產模組。不建立獨立 Maven 測試專案,Testcontainers 保持 test 依賴。nightly 以 Java 17/21 測試主機執行六個 Hadoop 版本,容器內 Java 依上表選擇,並儲存報告與容器日誌。 +測試原始碼位於實際生產模組的 `src/test`。共用策略單元測試在 common 中執行,共用原生單元測試在 `agents/grantforge-agent-hdfs-native/src/test` 中執行,不再編譯進六個版本代理。各版回呼測試仍在對應的編號模組中,共用的容器測試原始碼 `agents/grantforge-agent-hdfs-common/src/test/shared` 仍編譯到各編號模組;沒有獨立的 Maven 測試專案。Testcontainers 僅為 test scope。nightly 在 Java 17 與 21 主機上驗證六個 Hadoop 版本,Hadoop 容器使用上表中的 JVM。目前容器涵蓋 Simple 認證、手動 HA、全部六個版本線的 Kerberos 與 HTTPS、2.10 至 3.5 的自動 HA,以及 Hadoop 3.5.0 上 Kerberos 下的自動 HA;2.7 的自動 HA、其他版本在 Kerberos 下的自動 HA 與廠商修補仍需要在目標環境驗證。 -Hadoop 擴充入口與權限語意見 [Apache Hadoop 3.5.0 API](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/build/source/hadoop-hdfs-project/hadoop-hdfs/target/api/org/apache/hadoop/hdfs/server/namenode/INodeAttributeProvider.html) 和 [HDFS 權限指南](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/HdfsPermissionsGuide.html)。 +Hadoop 擴充入口與權限語意見 [Apache Hadoop 3.5.0 API](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/build/source/hadoop-hdfs-project/hadoop-hdfs/target/api/org/apache/hadoop/hdfs/server/namenode/INodeAttributeProvider.html) 與 [HDFS 權限指南](https://hadoop.apache.org/docs/r3.5.0/hadoop-project-dist/hadoop-hdfs/HdfsPermissionsGuide.html)。 diff --git a/docs/content/zh-tw/guide/catalog.md b/docs/content/zh-tw/guide/catalog.md index 69b54a16..1e3483cd 100644 --- a/docs/content/zh-tw/guide/catalog.md +++ b/docs/content/zh-tw/guide/catalog.md @@ -22,6 +22,12 @@ description: 維護應用程式與資源樹、資源相依、OAuth 用戶端, - **OAuth 用戶端**:為業務應用程式註冊用戶端,見 [OAuth 2.1 與 OpenID Connect](/zh-tw/integration/oauth/)。 - **欄位**:選取欄位時顯示它出現在哪些介面中(回傳或接收)。 +在有資源的應用程式目錄中點選 **匯出**,可下載 JSON 檔案供開發或 AI 工具參考。檔案包含應用程式資訊、依層級組織的完整資源與資源依賴,並附上依賴兩端的資源編碼;搜尋篩選不會影響匯出內容。`formatVersion` 標示匯出格式的版本。 + +選擇目標應用程式後,點選 **匯入**,上傳版本 1 的匯出檔案並先預檢。匯入依資源編碼重用同一應用程式中類型與上層一致的資源,不覆寫既有設定;類型、上層或依賴類型衝突時整個匯入不生效。新資源與依賴在確認後一次寫入,重複匯入不會建立重複項目。內建應用程式不支援匯入;宣告式依賴匯入後成為手動依賴。匯入資源需要資源建立權限,匯入依賴還需要資源更新權限。 + +匯出只包含 GrantForge 已登錄的資訊:頁面等資源的 `route` 是前端路由,API 資源的 `code` 是權限編碼;外部業務介面的 HTTP 方法與請求路徑若未登錄,不會自動推斷。JSON 是匯出當下的快照,不包含任何使用者的授權結果,資源有變化時需要重新匯出。 + ## API 目錄 **平台管理 → API 目錄** 列出伺服器端啟動時自動登記的全部介面及其存取要求:公開、登入即可,或需要某個權限代碼。需授權的介面依權限代碼歸入資源目錄,角色授權時引用這些權限。介面新增、下線或權限代碼變更時會列為「待確認的變更」,確認後更新目錄。 diff --git a/docs/content/zh-tw/guide/explain.md b/docs/content/zh-tw/guide/explain.md index b2737e05..10d0b22a 100644 --- a/docs/content/zh-tw/guide/explain.md +++ b/docs/content/zh-tw/guide/explain.md @@ -13,6 +13,8 @@ description: 檢視一個人能用什麼以及為什麼,模擬角色變更, 在 **使用者管理** 的使用者列上點擊「查看有效權限」,可以看到該使用者現在能用的一切:角色、選單與按鈕、API、資料範圍與受限欄位。 +選擇應用程式可檢視該應用程式的有效資源與 API 權限;「說明」顯示角色授權與使用者直接授權的來源。角色、資料範圍與欄位權限顯示使用者的整體設定。模擬新增或移除角色時,既有的直接授權會保留。 + ![有效權限](/screenshots/user-permissions.png) 點擊任一項目的 **說明**,GrantForge 會回答「為什麼能用」:由哪個角色、經過哪些分配(直接、使用者群組、部門、職位)與哪些資源推導而得;不能用時說明是沒有授權,還是被某條拒絕規則擋住。 diff --git a/docs/content/zh-tw/guide/identity-sources.md b/docs/content/zh-tw/guide/identity-sources.md index 370da8fb..36b69295 100644 --- a/docs/content/zh-tw/guide/identity-sources.md +++ b/docs/content/zh-tw/guide/identity-sources.md @@ -42,8 +42,13 @@ description: 讓使用者以公司目錄(LDAP/AD)或 OpenID Connect 提供 | --- | --- | | Issuer 位址 | 提供者的簽發者位址,GrantForge 透過它的發現文件取得端點與金鑰 | | 客戶端 ID / 金鑰 | 在提供者註冊的客戶端;沒有金鑰時作為公開客戶端,使用 PKCE | -| 回呼位址 | 頁面上顯示的 `/api/v1/auth/federated/callback/<编码>`,需要註冊到提供者的客戶端裡 | +| 回呼位址 | 頁面上顯示的 `/api/v1/auth/federated/callback/<編碼>`,需要註冊到提供者的客戶端裡 | | 範圍與宣告 | 預設 `openid profile email`;使用者名稱、顯示名稱、電子郵件分別取 `preferred_username`、`name`、`email` 宣告 | +| 手機號碼宣告 | 預設讀取 `phone`(Casdoor);其他提供者可設定為 `phone_number`。要同步手機號碼,scope 需包含 `phone`。登入時若提供者未回傳號碼,保留既有號碼 | +| 頭像位址 | 從 OIDC 標準 `picture` 宣告同步,scope 需包含 `profile`;未回傳時保留既有頭像位址 | +| 授權請求附加參數 | 選填的 JSON 物件,值必須為字串。例如 Casdoor 可填寫 `{"provider_hint":"github"}`,指定已設定的提供者;參數會出現在瀏覽器網址中,請勿填寫金鑰 | + +協定關鍵參數(包括 `state`、`nonce`、`redirect_uri` 與 PKCE 參數)由 GrantForge 管理,無法透過附加參數覆寫。 啟用後,登入頁出現「透過 <名稱> 登入」按鈕。使用者在提供者登入後回到 GrantForge;已開啟雙因素驗證的帳號還要輸入驗證碼。 @@ -54,3 +59,26 @@ description: 讓使用者以公司目錄(LDAP/AD)或 OpenID Connect 提供 - 不會自動建立帳號的身分來源僅能讓已關聯的使用者登入。 - 停用身分來源後,它的使用者無法登入;仍有帳號在使用的身分來源不能刪除。 - 身分來源帳號可以照常指派角色、開啟雙因素驗證。 + +組織架構同步在獨立的資料來源中設定,不需要修改 OIDC 登入設定。 + +## 資料來源與組織架構同步 + +資料來源負責組織架構同步,身分來源繼續負責 OIDC 或 LDAP 登入。兩者獨立設定,停用資料來源不影響 OIDC 登入。 + +1. 在「存取控制 → 身分來源」中設定現有的 OpenID Connect 登入。 +2. 在「存取控制 → 資料來源」中新增釘釘資料來源,選擇關聯的 OIDC 身分來源。 +3. 填寫 CorpId、AppKey、AppSecret 與起始部門 ID(通常為 `1`)。AppSecret 單獨加密保存,介面不會回傳明文,編輯時留空即保留原值。 +4. 可掛載到本地部門;同步間隔預設 30 分鐘,允許 15–10080 分鐘,清空表示只手動同步。 +5. 儲存後測試連線並執行首次同步。釘釘應用程式需有部門及員工通訊錄讀取權限,其授權範圍應涵蓋所選子樹。 +6. 使用者透過 SSO 登入後,依可信的 OIDC 電子郵件比對員工,自動填入部門與主部門。電子郵件必須由管理員維護、員工無法自行冒用,並與釘釘電子郵件一致。員工電子郵件缺失時使用企業電子郵件。 + +登入只讀取本地通訊錄快照,不請求釘釘。開啟 MFA 時,在第二步驗證成功後才更新歸屬。首次同步前登入的帳號會暫存可信電子郵件,同步完成後再補上關聯;本地可編輯的個人資料電子郵件不參與比對。缺失、重複或已被其他帳號綁定的電子郵件不會建立新綁定。 + +員工綁定以外部員工 ID 保持穩定,不會因電子郵件變更而轉移到另一位員工。同步涵蓋多部門歸屬;主部門優先保留仍有效的原主部門,否則依穩定的部門 ID 順序選擇。本地部門、使用者群組、職位與直接角色授權都會保留。將角色指派給同步部門後,使用者依現有授權規則繼承角色,調動部門會更新權限。 + +已有組織快照或待關聯帳號後,關聯的身分來源、企業、應用程式、起始部門與掛載位置即固定。同步的部門與歸屬為唯讀,無法從使用者或組織管理中手動覆寫。消失的部門會保留,以保護既有授權;員工離開授權通訊錄後,移除此資料來源管理的歸屬,但不會自動停用帳號。擁有同步部門的資料來源不能直接刪除,可改為停用。 + +同步取得完整分頁後再以原子方式套用。讀取失敗、層級異常或授權範圍變化時保留上次成功的快照,管理員可透過同步摘要、異常清單與稽核記錄檢查結果。授權範圍變化時,需先恢復原本核准的範圍再同步。 + +目前使用[釘釘官方服務端 SDK](https://open.dingtalk.com/document/development/download-the-server-side-sdk),沒有自行實作 HTTP 呼叫。供應商配接器輸出統一的部門與員工快照,之後可增加飛書配接器,重用相同的同步、綁定與權限更新邏輯。 diff --git a/docs/content/zh-tw/guide/roles.md b/docs/content/zh-tw/guide/roles.md index f5acaab0..f0db583a 100644 --- a/docs/content/zh-tw/guide/roles.md +++ b/docs/content/zh-tw/guide/roles.md @@ -15,7 +15,7 @@ description: 建立角色,授予頁面、按鈕與介面,設定繼承,把 ## 系統角色與自訂角色 -每個租戶都有系統角色 **租戶管理員**,平台租戶還有 **平台管理員**。系統角色自動擁有其模組下的全部資源,不能修改;需要類似但更小的權限时,**複製**系統角色,再修改副本。 +每個租戶都有系統角色 **租戶管理員**,平台租戶還有 **平台管理員**。系統角色自動擁有其模組下的全部資源,不能修改;需要類似但更小的權限時,**複製**系統角色,再修改副本。 自訂角色有代碼(小寫字母、數字、點、連字號或底線)和名稱,可以停用:停用的角色不授予任何權限,也不透過繼承傳遞權限。 diff --git a/docs/content/zh-tw/guide/users.md b/docs/content/zh-tw/guide/users.md index 23dfa83e..f9233eb6 100644 --- a/docs/content/zh-tw/guide/users.md +++ b/docs/content/zh-tw/guide/users.md @@ -31,6 +31,7 @@ description: 建立、尋找、編輯、停用、鎖定與刪除帳號,重設 | 重設雙因素驗證 | 用於驗證器遺失:關閉該帳號的雙因素驗證並結束其工作階段 | | 檢視角色 | 帳號持有的角色以及每個角色的來源(直接指派、使用者群組、部門、職位) | | 檢視有效權限 | 見 [權限解釋、模擬與稽核](/zh-tw/guide/explain/) | +| 直接授權 | 選擇業務應用程式,為使用者單獨允許選單、頁面、按鈕或介面,可設定到期時間 | | 刪除 | 刪除帳號及其部門關聯,稽核記錄會保留 | 系統帳號(初始化時建立的管理員)和你自己的帳號不能被停用、鎖定或刪除。 @@ -38,3 +39,5 @@ description: 建立、尋找、編輯、停用、鎖定與刪除帳號,重設 ## 自助註冊 預設關閉。設定 `grantforge.security.registration-enabled=true` 後,登入頁會出現「建立帳號」入口,註冊的帳號會進入 `grantforge.security.registration-tenant` 指定的租戶,是沒有任何角色的一般帳號。 + +直接授權與角色權限共同計算,角色中的拒絕仍然優先,資源依賴依現有規則推導。直接授權不受以角色為基礎的職責分離限制,不授予資料列或欄位策略,也不能用於主控台應用程式。只有具備角色授權權限且能修改目標使用者的管理員可以操作。 diff --git a/docs/content/zh-tw/integration/javascript.md b/docs/content/zh-tw/integration/javascript.md index 6064ace8..3d11fb69 100644 --- a/docs/content/zh-tw/integration/javascript.md +++ b/docs/content/zh-tw/integration/javascript.md @@ -48,7 +48,8 @@ app.use(createGrantForge(grantForge)) ```vue - ``` + +``` `useGrantForge()` 提供 `authorization`、`error`、`refresh()`、`can()`、`hasResource()`。隱藏按鈕僅是便利,應用程式的 API 必須同樣驗證權限。 diff --git a/docs/content/zh-tw/start/quick-start.md b/docs/content/zh-tw/start/quick-start.md index b08be9eb..407fd335 100644 --- a/docs/content/zh-tw/start/quick-start.md +++ b/docs/content/zh-tw/start/quick-start.md @@ -38,7 +38,7 @@ WARN ... First-run setup is pending. Open the console and enter this setup toke ## 2. 完成初始化 -以瀏覽器開啟 http://127.0.0.1:9999/,主控台會自動進入初始化頁面。填寫日誌中的權杖、組織名稱以及第一位管理員的使用者名稱和密碼(至少 12 個字元)。 +以瀏覽器開啟 ,主控台會自動進入初始化頁面。填寫日誌中的權杖、組織名稱以及第一位管理員的使用者名稱和密碼(至少 12 個字元)。 ![首次初始化與登入頁面](/screenshots/login.png) diff --git a/docs/content/zh/changelog/2026.4.0.md b/docs/content/zh/changelog/2026.4.0.md new file mode 100644 index 00000000..17c04a28 --- /dev/null +++ b/docs/content/zh/changelog/2026.4.0.md @@ -0,0 +1,68 @@ +--- +title: "2026.4.0" +description: "GrantForge 2026.4.0(2026-11-08):Elasticsearch、Apache Doris、MongoDB 与 Redis/Valkey 的服务插件及代理,钉钉组织架构同步,用户直接授权,资源目录导入导出,以及检查每种语言翻译完整性的 CI。" +--- + + +2026.4.0 将外部系统权限扩展到 Elasticsearch、Apache Doris、MongoDB 以及 Redis 与 Valkey,四个代理都运行在系统旁边,让系统自身的访问控制与策略保持一致。身份与授权方面新增钉钉组织架构同步、用户直接授权和资源目录的导入导出。完整变更见 [GitHub 对比](https://github.com/devlive-community/grantforge/compare/v2026.3.0...v2026.4.0)。 + +## Elasticsearch + +- Elasticsearch 服务插件:索引与数据流、其字段以及集群;支持密码、API 密钥或 TLS 客户端证书登录;见 [Elasticsearch](/plugins/elasticsearch/)。 +- 运行在集群旁的代理:为每个用户维护 Elasticsearch 自己的角色,赋予原生用户,并通过角色映射给其他 realm 的用户;许可证允许时带有字段级与文档级安全;索引模式保持为模式,新索引立即可用;见 [Elasticsearch 代理](/external/elasticsearch-agent/)。 +- 访问记录来自审计日志文件。集成测试覆盖 Elasticsearch 8.19、9.3、9.4 与 9.5。 + +## Apache Doris + +- Doris 服务插件:Catalog、数据库、表、列与工作负载组;连接一个或多个 FE,以密码登录;见 [Apache Doris](/plugins/doris/)。 +- 运行在集群旁的代理:策略成为 Doris 自己的角色、列授权、工作负载组授权与行策略,并授予用户的每个身份;只有在 Doris 不会因此给出更多权限时,才授予整个数据库或 Catalog;见 [Apache Doris 代理](/external/doris-agent/)。 +- 访问记录来自 Doris 的审计日志表。集成测试覆盖 Doris 2.1、3.1、4.0 与 4.1。 + +## MongoDB + +- MongoDB 服务插件:数据库与集合;连接副本集或分片集群,支持 SCRAM、客户端证书、LDAP 或 Kerberos 登录;见 [MongoDB](/plugins/mongodb/)。 +- 运行在部署旁的代理:为每个用户维护一个 MongoDB 角色,一步替换其权限;通过 MongoDB 给出的继承权限,报告 GrantForge 之外的内置与自定义角色实际给了什么;见 [MongoDB 代理](/external/mongodb-agent/)。 +- 访问记录来自 Enterprise 或 Percona 的审计日志文件,或来自 Profiler。集成测试覆盖 MongoDB 6.0、7.0、8.0 与 9.0,Kerberos 在 Percona Server for MongoDB 上验证。 + +## Redis 与 Valkey + +- Redis 服务插件:键、频道与节点;连接单机及其副本或集群,以 ACL 用户登录,可选 TLS;见 [Redis](/plugins/redis/)。 +- 运行在部署旁的代理:在每个节点上让每个用户的 ACL 选择器与策略保持一致,并用一条命令整体替换;根权限仍由运维人员管理,代理用 `ACL DRYRUN` 询问 Redis 这些权限是否允许了策略拒绝的访问;见 [Redis 代理](/external/redis-agent/)。 +- 拒绝记录来自各节点的 ACL 日志。集成测试覆盖 Redis 7.2、7.4、8.2、8.10 与 Valkey 8.1、9.1,以及一个 Redis 8.10 集群。 + +## 代理 + +- ClickHouse、PostgreSQL、OpenSearch 以及本版新增的系统旁代理共用新的 `grantforge-agent-sync` 核心:启动器、设置、定时器、同步流程、审计读取位置与日志文件跟踪;各代理只保留其系统特有的部分,设置与日志格式不变。 + +## 身份与组织 + +- 数据源:组织架构同步独立于身份源配置,首个适配器为钉钉,使用其官方服务端 SDK;用户通过 SSO 登录后按可信邮箱自动关联部门与主部门;其数据表在全部六种支持的数据库上迁移,包括 SQL Server 与 Oracle;见 [身份源](/guide/identity-sources/)。 +- OpenID Connect 身份源可以同步手机号与头像,并可在授权请求中附加参数(例如 Casdoor 的 `provider_hint`),协议自身的参数不能被覆盖。 + +## 授权与资源目录 + +- 用户直接授权:为单个用户允许业务应用的菜单、页面、按钮或接口,可设置到期时间,与角色权限一起计算,角色中的拒绝仍然优先;见 [用户](/guide/users/)。 +- 资源目录可以把应用的资源树导出为 JSON,再导入另一个应用:先预检,冲突时整个导入不生效;平台管理员可以清空符合条件的应用资源树;见 [资源目录](/guide/catalog/)。 + +## 服务端 + +- 启动时打印 GrantForge 的横幅、版本与 Java 版本,不再是 Spring Boot 的横幅。 + +## 文档与发布 + +- 插件页以总览开始,按系统类型分组并可折叠,列出每个系统的执行方式、行过滤与脱敏支持以及验证过的版本;各 README 按同样的顺序列出组件。 +- CI 检查每个页面在每种语言中的翻译是否完整:段落、标题、表格、代码块与链接与中文页一致,不含其他语言的文字,也不照抄英文页;11 个 README 按英文 README 做同样的检查。检查发现的缺失翻译已全部补齐。 +- 所有 README 的版本徽章都由版本检查维护。 +- 在 fork 上不再运行发布文档与发布版本的工作流。 +- 服务端 `grantforge-server` 不再发布到 Maven 仓库:它包含全部插件,超出了仓库的大小限制;服务端随发行包与镜像提供。 + +## 问题修复 + +- 在资源编辑框中改变资源类型时,清除不再适用的资源编码。 +- JavaScript 接入指南的最后一个代码块不再吞掉后面的正文,Docker 与快速开始页面中的控制台地址不再把后面的文字当作链接。 +- 依赖升级到修复安全公告的版本:MariaDB Connector/J 3.5.11、OpenTelemetry 1.67.0、Guava 33.7.2、Snappy 1.1.10.9,以及控制台与 SDK 中的 vue-i18n、glob 与 minimatch。 diff --git a/docs/content/zh/deploy/docker.md b/docs/content/zh/deploy/docker.md index a1004f92..1f23ca7f 100644 --- a/docs/content/zh/deploy/docker.md +++ b/docs/content/zh/deploy/docker.md @@ -43,7 +43,7 @@ docker compose -f deploy/compose/postgres.yml up -d docker compose -f deploy/compose/postgres.yml logs grantforge | grep "setup token" ``` -然后打开 http://127.0.0.1:9999/。示例使用的默认数据库密码只适合试用,正式使用前请通过 `GRANTFORGE_DB_PASSWORD` 修改。MySQL 示例需要先把 `mysql-connector-j-<版本>.jar` 放进 `deploy/compose/drivers/`。 +然后打开 。示例使用的默认数据库密码只适合试用,正式使用前请通过 `GRANTFORGE_DB_PASSWORD` 修改。MySQL 示例需要先把 `mysql-connector-j-<版本>.jar` 放进 `deploy/compose/drivers/`。 ## Helm diff --git a/docs/content/zh/develop/development.md b/docs/content/zh/develop/development.md index e4023427..4829ff23 100644 --- a/docs/content/zh/develop/development.md +++ b/docs/content/zh/develop/development.md @@ -76,7 +76,7 @@ bash script/release/tag.sh 2026.1.0 --next 2026.2.0 # 设置版本、打标 - `script/ci/release.sh` 构建发行包、只含发行依赖的 CycloneDX SBOM 与 `SHA256SUMS`; - 多架构镜像推送到 `ghcr.io/devlive-community/grantforge`; -- Maven 构件(含源码包与 Javadoc)发布到 GitHub Packages;仓库配置了 `CENTRAL_USERNAME`、`CENTRAL_PASSWORD`(Central Portal 令牌)、`GPG_PRIVATE_KEY` 与 `GPG_PASSPHRASE` 时,签名后发布到 Maven Central; +- Maven 构件(含源码包与 Javadoc)发布到 GitHub Packages;仓库配置了 `CENTRAL_USERNAME`、`CENTRAL_PASSWORD`(Central Portal 令牌)、`GPG_PRIVATE_KEY` 与 `GPG_PASSPHRASE` 时,签名后发布到 Maven Central;服务端 `grantforge-server` 打包了全部插件,超出仓库的大小限制,不发布到任何 Maven 仓库,只随发行包与镜像提供; - 创建 GitHub Release,正文是上一个发布版本(`v*` 或 `1.0.6` 这样的数字标签)以来的全部提交,按新功能、问题修复、性能等分组,附提交链接。 候选版标记为预发布,不更新镜像的 `latest`。本地启用 `central` profile 默认不会发布(`central.skip=true`),只有发布工作流显式传入 `-Dcentral.skip=false`。 @@ -100,3 +100,5 @@ pnpm build # 输出到 docs/out ``` 页面是 `docs/content/` 下的 Markdown,导航在 `docs/lib/navigation.ts`。API 参考与错误码在构建时从契约与源代码生成。截图由 `script/docs/screenshots.sh` 启动真实服务、写入示例数据后用 Playwright 生成。 + +`pnpm test` 同时核对翻译:每个中文页面在其他十种语言都要有译文,段落、标题层级、列表、表格、代码块与链接都与中文页一致;页面中不能出现不属于该语言的文字(包括繁体与日文中的简体字),也不能照抄英文页的句子。仓库根目录的 11 个 README 以英文 README 为准做同样的检查,指向本站的链接必须是对应语言的现有页面。 diff --git a/docs/content/zh/integration/javascript.md b/docs/content/zh/integration/javascript.md index dd7f81e3..ca9c5a08 100644 --- a/docs/content/zh/integration/javascript.md +++ b/docs/content/zh/integration/javascript.md @@ -48,7 +48,8 @@ app.use(createGrantForge(grantForge)) ```vue - ``` + +``` `useGrantForge()` 提供 `authorization`、`error`、`refresh()`、`can()`、`hasResource()`。隐藏按钮只是便利,应用的 API 必须同样校验权限。 diff --git a/docs/content/zh/start/quick-start.md b/docs/content/zh/start/quick-start.md index b0d2f4d8..a5801005 100644 --- a/docs/content/zh/start/quick-start.md +++ b/docs/content/zh/start/quick-start.md @@ -38,7 +38,7 @@ WARN ... First-run setup is pending. Open the console and enter this setup toke ## 2. 完成初始化 -浏览器打开 http://127.0.0.1:9999/,控制台会自动进入初始化页面。填写日志中的令牌、组织名称以及第一个管理员的用户名和密码(至少 12 个字符)。 +浏览器打开 ,控制台会自动进入初始化页面。填写日志中的令牌、组织名称以及第一个管理员的用户名和密码(至少 12 个字符)。 ![首次初始化与登录页](/screenshots/login.png) diff --git a/docs/lib/navigation.ts b/docs/lib/navigation.ts index d31ac341..7d8b99f1 100644 --- a/docs/lib/navigation.ts +++ b/docs/lib/navigation.ts @@ -581,6 +581,7 @@ const changelog: NavSection = { descriptionJa: '各バージョンでの変更点。', groups: [ { title: '版本', en: 'Versions', de: 'Versionen', fr: 'Versions', es: 'Versiones', pt: 'Versões', it: 'Versioni', ru: 'Версии', tw: '版本', ko: '버전', ja: 'バージョン', pages: [ + { slug: 'changelog/2026.4.0', title: '2026.4.0', en: '2026.4.0', de: '2026.4.0', fr: '2026.4.0', es: '2026.4.0', pt: '2026.4.0', it: '2026.4.0', ru: '2026.4.0', tw: '2026.4.0', ko: '2026.4.0', ja: '2026.4.0' }, { slug: 'changelog/2026.3.0', title: '2026.3.0', en: '2026.3.0', de: '2026.3.0', fr: '2026.3.0', es: '2026.3.0', pt: '2026.3.0', it: '2026.3.0', ru: '2026.3.0', tw: '2026.3.0', ko: '2026.3.0', ja: '2026.3.0' }, { slug: 'changelog/2026.2.0', title: '2026.2.0', en: '2026.2.0', de: '2026.2.0', fr: '2026.2.0', es: '2026.2.0', pt: '2026.2.0', it: '2026.2.0', ru: '2026.2.0', tw: '2026.2.0', ko: '2026.2.0', ja: '2026.2.0' }, { slug: 'changelog/2026.1.0', title: '2026.1.0', en: '2026.1.0', de: '2026.1.0', fr: '2026.1.0', es: '2026.1.0', pt: '2026.1.0', it: '2026.1.0', ru: '2026.1.0', tw: '2026.1.0', ko: '2026.1.0', ja: '2026.1.0' }, diff --git a/docs/lib/translations.ts b/docs/lib/translations.ts new file mode 100644 index 00000000..76be2cc2 --- /dev/null +++ b/docs/lib/translations.ts @@ -0,0 +1,173 @@ +// Copyright (c) 2026 devlive-community/grantforge +// +// Licensed under the MIT License. See the LICENSE file in the +// project root for full license text. + +import matter from 'gray-matter' +import type { Nodes, Root } from 'mdast' +import remarkGfm from 'remark-gfm' +import remarkParse from 'remark-parse' +import { unified } from 'unified' +import { visit } from 'unist-util-visit' +import { type Locale, stripLocale } from '@/lib/i18n' + +/** + * What a translation must keep of its source page: the parts a reader navigates by or copies, which a translator + * has no reason to add, drop or reorder. + */ +export interface Outline { + /** The kind of every top-level block in page order ('paragraph', 'list'), so no paragraph is dropped or added. */ + blocks: string[] + /** Heading levels in page order ('h2'). */ + headings: string[] + /** The language of every fenced block in page order ('mermaid', '' when none is given). */ + code: string[] + /** Every table as rows x columns, the header row included. */ + tables: string[] + /** The number of items of every list, in page order. */ + lists: number[] + /** Link and image targets in page order, without the language prefix and the site's own origin. */ + links: string[] +} + +/** Where the published site lives; README links to it count as internal ones. */ +export const SITE = 'https://grantforge.devlive.org' + +/** + * A link to a README is part of a README's language switcher, which links every language but its own and so + * differs in every translation by design. + */ +const SWITCHER_TARGET = /^README[\w.-]*\.md$/ + +const parser = unified().use(remarkParse).use(remarkGfm) + +/** The target of a link with the language prefix (and the site's origin) removed, so all languages compare equal. */ +export function neutralTarget(url: string): string { + const local = url.startsWith(SITE) ? url.slice(SITE.length) || '/' : url + return local.startsWith('/') ? `/${stripLocale(local.slice(1))}` : local +} + +/** A page split into its front matter and the syntax tree of its body. */ +interface Parsed { + data: Record + tree: Root +} + +/** The last pages parsed: a check reads each page's outline and sentences, and each English page once per language. */ +const recent = new Map() + +function parse(markdown: string): Parsed { + const known = recent.get(markdown) + if (known) return known + const { data, content } = matter(markdown) + const parsed = { data, tree: parser.parse(content) as Root } + recent.set(markdown, parsed) + if (recent.size > 8) recent.delete(recent.keys().next().value ?? '') + return parsed +} + +/** Reads the outline of a page (front matter and comments are not part of it). */ +export function outline(markdown: string): Outline { + const { tree } = parse(markdown) + const result: Outline = { blocks: tree.children.map(node => node.type), headings: [], code: [], tables: [], lists: [], links: [] } + visit(tree, (node: Nodes) => { + switch (node.type) { + case 'heading': result.headings.push(`h${node.depth}`); break + case 'code': result.code.push(node.lang ?? ''); break + case 'table': result.tables.push(`${node.children.length}x${node.children[0]?.children.length ?? 0}`); break + case 'list': result.lists.push(node.children.length); break + case 'link': + case 'image': if (!SWITCHER_TARGET.test(node.url)) result.links.push(neutralTarget(node.url)); break + } + }) + return result +} + +/** Explains how a translation's outline differs from its source's; empty when they agree. */ +export function compareOutlines(source: Outline, translation: Outline): string[] { + const problems: string[] = [] + for (const part of ['blocks', 'headings', 'code', 'tables', 'lists', 'links'] as const) { + const expected = source[part].map(String) + const actual = translation[part].map(String) + if (expected.join('\n') === actual.join('\n')) continue + const at = expected.findIndex((value, index) => value !== actual[index]) + const index = at < 0 ? expected.length : at + problems.push(`${part}: ${expected.length} in the source, ${actual.length} here; first difference at #${index + 1}` + + ` (source ${expected[index] ?? 'nothing'}, here ${actual[index] ?? 'nothing'})`) + } + return problems +} + +/** Writing systems that belong to one language only. */ +const SCRIPTS = [ + { name: 'Chinese characters', pattern: /\p{Script=Han}/u, allowed: ['zh', 'zh-tw', 'ja'] }, + { name: 'Hangul', pattern: /\p{Script=Hangul}/u, allowed: ['ko'] }, + { name: 'Kana', pattern: /[\p{Script=Hiragana}\p{Script=Katakana}]/u, allowed: ['ja'] }, + { name: 'Cyrillic', pattern: /\p{Script=Cyrillic}/u, allowed: ['ru'] }, +] as const satisfies readonly { name: string, pattern: RegExp, allowed: readonly Locale[] }[] + +/** + * Frequent characters that exist in simplified Chinese only: neither Traditional Chinese nor Japanese writes them + * (they use 這, 時, 權, 証 …), so finding one in those languages means a sentence was left in the source. Shapes + * the three share, such as 号, 数, 会, 点 or 条, are deliberately left out. + */ +const SIMPLIFIED_ONLY = new Set('这们个时为对发进过动关单务应设认证权组织户账码库测试录项显览导页请选择删编辑资访问规则统级' + + '实现档开启闭间结构节线络缓读获签钥详细门岗变历审计种标报错误运维复联类带处产业么还让从该样无') + +/** A line linking another README is the language switcher, which names every language in its own script. */ +const LANGUAGE_SWITCHER = /\]\(README[\w.-]*\.md\)/ + +/** Whether a paragraph links another README, which makes it the language switcher. */ +function isSwitcher(node: Nodes): boolean { + return node.type === 'link' ? SWITCHER_TARGET.test(node.url) : 'children' in node && node.children.some(isSwitcher) +} + +/** Lines (1-based, front matter and code included) that use a script foreign to the locale, with the reason. */ +export function foreignScripts(markdown: string, locale: Locale): string[] { + const problems: string[] = [] + markdown.split('\n').forEach((line, index) => { + if (LANGUAGE_SWITCHER.test(line)) return + const found: string[] = SCRIPTS + .filter(script => !(script.allowed as readonly Locale[]).includes(locale) && script.pattern.test(line)) + .map(script => script.name) + if (locale === 'zh-tw' || locale === 'ja') { + const simplified = [...new Set([...line].filter(character => SIMPLIFIED_ONLY.has(character)))] + if (simplified.length) found.push(`simplified Chinese (${simplified.join('')})`) + } + if (found.length) problems.push(`line ${index + 1}: ${found.join(', ')}: ${line.trim().slice(0, 80)}`) + }) + return problems +} + +/** At least this many words make a sentence that cannot be the same in two languages by chance. */ +const SENTENCE_WORDS = 5 + +/** + * The sentences of a page a translator writes: title, description, headings, paragraphs and table cells, without + * code and link targets, reduced to their words so markup and punctuation do not matter. + */ +export function sentences(markdown: string): Set { + const { data, tree } = parse(markdown) + const found = new Set() + const add = (text: string) => { + const words = text.match(/\p{L}{2,}/gu) ?? [] + if (words.length >= SENTENCE_WORDS) found.add(words.join(' ')) + } + add(String(data.title ?? '')) + add(String(data.description ?? '')) + const text = (node: Nodes): string => node.type === 'text' ? node.value + : node.type === 'inlineCode' || node.type === 'code' || node.type === 'html' ? ' ' + : 'children' in node ? node.children.map(text).join('') : '' + visit(tree, (node: Nodes) => { + if (node.type === 'paragraph' || node.type === 'heading' || node.type === 'tableCell') { + if (!isSwitcher(node)) add(text(node)) + return 'skip' + } + }) + return found +} + +/** The sentences a translation copied unchanged from the English page (given as its sentences) instead of translating them. */ +export function copiedSentences(translation: string, english: Set): string[] { + return [...sentences(translation)].filter(sentence => english.has(sentence)) +} diff --git a/docs/scripts/check-content.mjs b/docs/scripts/check-content.mjs index 8b0680b1..46d736ce 100644 --- a/docs/scripts/check-content.mjs +++ b/docs/scripts/check-content.mjs @@ -6,8 +6,8 @@ // Checks the documentation before it is built: every page in the navigation has a file with a title and a // description, every Markdown file is in the navigation, and every internal link and image points at something // that exists. Every language has a directory below content/, Chinese included; a translated page must mirror -// the Chinese one and link within its own language, while a missing translation is fine (it falls back at -// build time). Run with `pnpm check`. +// the Chinese one and link within its own language. A missing translation falls back at build time, but +// test/translations.test.ts requires every page to be translated completely. Run with `pnpm check`. import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs' import { join, relative } from 'node:path' diff --git a/docs/test/translations.test.ts b/docs/test/translations.test.ts new file mode 100644 index 00000000..a4665e91 --- /dev/null +++ b/docs/test/translations.test.ts @@ -0,0 +1,177 @@ +// Copyright (c) 2026 devlive-community/grantforge +// +// Licensed under the MIT License. See the LICENSE file in the +// project root for full license text. + +import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs' +import { join, relative } from 'node:path' +import { describe, expect, it } from 'vitest' +import { CONTENT_DIR } from '@/lib/content' +import { type Locale, LOCALES, localeDir, pageHref, stripLocale } from '@/lib/i18n' +import { pages } from '@/lib/navigation' +import { SITE, compareOutlines, copiedSentences, foreignScripts, neutralTarget, outline, sentences } from '@/lib/translations' + +const SOURCE = `--- +title: 用户 +description: 管理账号。 +--- + + + +## 操作 + +| 操作 | 效果 | +| --- | --- | +| 禁用 | 无法登录 | + +1. 打开 [用户](/guide/users/) +2. 看图 ![图](/images/a.png) + +\`\`\`mermaid +flowchart LR +\`\`\` +` + +/** The repository root, where the README files are. */ +const REPOSITORY = join(CONTENT_DIR, '..', '..') + +/** The language of every README, and the README every other one translates. */ +const READMES: Record = { + 'README.md': 'en', 'README.zh-CN.md': 'zh', 'README.zh-TW.md': 'zh-tw', 'README.ru.md': 'ru', 'README.ko.md': 'ko', + 'README.ja.md': 'ja', 'README.de.md': 'de', 'README.fr.md': 'fr', 'README.es.md': 'es', 'README.pt-BR.md': 'pt-br', + 'README.it.md': 'it', +} + +function markdownFiles(dir: string): string[] { + return readdirSync(dir).flatMap(name => { + const path = join(dir, name) + return statSync(path).isDirectory() ? markdownFiles(path) : name.endsWith('.md') ? [path] : [] + }) +} + +function read(path: string): string { + return readFileSync(path, 'utf8') +} + +describe('translations', () => { + it('reads the outline of a page without its front matter', () => { + expect(outline(SOURCE)).toEqual({ + blocks: ['html', 'heading', 'table', 'list', 'code'], + headings: ['h2'], code: ['mermaid'], tables: ['2x2'], lists: [2], links: ['/guide/users/', '/images/a.png'], + }) + }) + + it('compares links in every language and on the published site alike', () => { + expect(neutralTarget('/en/guide/users/')).toBe('/guide/users/') + expect(neutralTarget('/en/')).toBe('/') + expect(neutralTarget('/guide/users/')).toBe('/guide/users/') + expect(neutralTarget('https://grantforge.devlive.org/ja/plugins/yarn/')).toBe('/plugins/yarn/') + expect(neutralTarget('https://grantforge.devlive.org')).toBe('/') + expect(neutralTarget('https://example.com/en/x')).toBe('https://example.com/en/x') + expect(neutralTarget('LICENSE')).toBe('LICENSE') + }) + + it('names the first difference of every part a translation dropped or added', () => { + const translation = SOURCE.replace('| 禁用 | 无法登录 |\n', '').replace('## 操作', '## Actions\n\n### More') + .replace('](/guide/users/)', '](/en/guide/users/)') + expect(compareOutlines(outline(SOURCE), outline(translation))).toEqual([ + 'blocks: 5 in the source, 6 here; first difference at #3 (source table, here heading)', + 'headings: 1 in the source, 2 here; first difference at #2 (source nothing, here h3)', + 'tables: 1 in the source, 1 here; first difference at #1 (source 2x2, here 1x2)', + ]) + expect(compareOutlines(outline(SOURCE), outline(SOURCE))).toEqual([]) + }) + + it('finds text in a script the language does not write', () => { + expect(foreignScripts('Sign in\n`<编码>` here', 'en')).toEqual(['line 2: Chinese characters: `<编码>` here']) + expect(foreignScripts('Привет 안녕 カナ', 'de')).toEqual(['line 1: Hangul, Kana, Cyrillic: Привет 안녕 カナ']) + expect(foreignScripts('權限时', 'zh-tw')).toEqual(['line 1: simplified Chinese (时): 權限时']) + expect(foreignScripts('権限を設定する時', 'ja')).toEqual([]) + expect(foreignScripts('権限时', 'ja')).toEqual(['line 1: simplified Chinese (时): 権限时']) + expect(foreignScripts('권한 설정', 'ko')).toEqual([]) + expect(foreignScripts('Права доступа', 'ru')).toEqual([]) + expect(foreignScripts('设置权限', 'zh')).toEqual([]) + expect(foreignScripts('Language: [English](README.md) · [中文](README.zh-CN.md)', 'de')).toEqual([]) + }) + + it('finds sentences copied from the English page', () => { + const english = '---\ntitle: Users and their accounts here\ndescription: Short\n---\n\nThe account cannot sign in at all.\n\n' + + '| Setting | Meaning |\n| --- | --- |\n| Timeout | How long the server waits for an answer |\n\n```\nsame code in every language here\n```\n' + const german = english.replace('The account cannot sign in at all.', 'Das Konto kann sich gar nicht anmelden.') + expect(sentences(english)).toEqual(new Set(['Users and their accounts here', 'The account cannot sign in at all', + 'How long the server waits for an answer'])) + expect(copiedSentences(german, sentences(english))).toEqual(['Users and their accounts here', 'How long the server waits for an answer']) + expect(copiedSentences('Short words only, fine.', sentences('Short words only, fine.'))).toEqual([]) + }) + + const sourceDir = join(CONTENT_DIR, 'zh') + const sourcePages = markdownFiles(sourceDir).map(file => relative(sourceDir, file).replace(/\\/g, '/')) + // What every language compares against, read once: the outline of each Chinese page and the English sentences. + const shapes = new Map(sourcePages.map(page => [page, outline(read(join(sourceDir, page)))])) + const english = new Map(sourcePages.map(page => { + const file = join(CONTENT_DIR, 'en', page) + return [page, existsSync(file) ? sentences(read(file)) : new Set()] + })) + + it('writes the Chinese source pages in Chinese', () => { + const problems = sourcePages.flatMap(page => foreignScripts(read(join(sourceDir, page)), 'zh').map(problem => `zh/${page}: ${problem}`)) + expect(problems).toEqual([]) + }) + + it.each(LOCALES.filter(locale => locale !== 'zh'))('translates every page completely into %s', locale => { + const problems: string[] = [] + for (const page of sourcePages) { + const name = `${localeDir(locale)}/${page}` + const file = join(CONTENT_DIR, name) + if (!existsSync(file)) { + problems.push(`${name}: not translated`) + continue + } + const translation = read(file) + for (const problem of compareOutlines(shapes.get(page) ?? outline(''), outline(translation))) problems.push(`${name}: ${problem}`) + for (const problem of foreignScripts(translation, locale)) problems.push(`${name}: ${problem}`) + if (locale !== 'en') { + for (const sentence of copiedSentences(translation, english.get(page) ?? new Set())) { + problems.push(`${name}: copied from the English page: ${sentence.slice(0, 80)}`) + } + } + } + const dir = join(CONTENT_DIR, localeDir(locale)) + for (const file of markdownFiles(dir)) { + const page = relative(dir, file).replace(/\\/g, '/') + if (!sourcePages.includes(page)) problems.push(`${localeDir(locale)}/${page}: has no Chinese source page`) + } + expect(problems).toEqual([]) + }) + + it('keeps every README a complete translation of the English one', () => { + const problems: string[] = [] + const reference = read(join(REPOSITORY, 'README.md')) + const shape = outline(reference) + for (const [name, locale] of Object.entries(READMES)) { + const file = join(REPOSITORY, name) + if (!existsSync(file)) { + problems.push(`${name}: missing`) + continue + } + const readme = read(file) + if (locale !== 'en') { + for (const problem of compareOutlines(shape, outline(readme))) problems.push(`${name}: ${problem}`) + for (const sentence of copiedSentences(readme, sentences(reference))) problems.push(`${name}: copied from README.md: ${sentence.slice(0, 80)}`) + } + for (const problem of foreignScripts(readme, locale)) problems.push(`${name}: ${problem}`) + for (const [, url = ''] of readme.matchAll(/\]\((https:\/\/grantforge\.devlive\.org[^)\s]*)\)/g)) { + const path = url.slice(SITE.length) + if (path === '' || path === '/') continue + const slug = stripLocale(path.slice(1)).replace(/\/$/, '') + if (!pages.some(page => page.slug === slug)) problems.push(`${name}: ${url} is not a page of the documentation`) + else if (url !== `${SITE}${pageHref(slug, locale)}`) problems.push(`${name}: ${url} should be ${SITE}${pageHref(slug, locale)}`) + } + } + const others = readdirSync(REPOSITORY).filter(name => /^README.*\.md$/.test(name) && !(name in READMES)) + for (const name of others) problems.push(`${name}: a README in a language the documentation does not have`) + expect(problems).toEqual([]) + }) +}) diff --git a/pom.xml b/pom.xml index bfd39ae5..7d706210 100644 --- a/pom.xml +++ b/pom.xml @@ -799,8 +799,10 @@ ${central.skip} true published + grantforge-plugin-example + grantforge-server diff --git a/script/ci/changed_areas.py b/script/ci/changed_areas.py index 11a86347..e03fe2f1 100644 --- a/script/ci/changed_areas.py +++ b/script/ci/changed_areas.py @@ -63,8 +63,10 @@ def _integration(name: str, script: str, base: Sequence[str] = ()) -> List[str]: "script/ci/coverage_thresholds.txt"] + _CI, "web": ["core/grantforge-web/**"] + _PROSE + ["script/ci/web.sh"] + _CI, "sdk_js": ["sdk/grantforge-js/**"] + _PROSE + ["script/ci/sdk_js.sh"] + _CI, - "docs": ["docs/**", "core/grantforge-web/src/api/openapi.json", "core/**/src/main/java/**/*ErrorCode.java", - "core/**/src/main/resources/i18n/*.properties", "script/ci/docs.sh"] + _CI, + # The documentation's tests also check that the READMEs are complete translations of README.md. + "docs": ["docs/**", "README*.md", "core/grantforge-web/src/api/openapi.json", + "core/**/src/main/java/**/*ErrorCode.java", "core/**/src/main/resources/i18n/*.properties", + "script/ci/docs.sh"] + _CI, "database": _MAVEN + _SERVER + _PROSE + ["script/ci/db_integration.sh", "script/ci/check_migrations.py"] + _CI, "plugin_api": ["pom.xml", "core/grantforge-plugin-api/**"] + _PROSE + ["script/ci/check_plugin_api_compat.py"] + _CI, diff --git a/script/ci/check_versions.py b/script/ci/check_versions.py index 2c7e91b4..5b08a2cb 100755 --- a/script/ci/check_versions.py +++ b/script/ci/check_versions.py @@ -10,7 +10,7 @@ release candidate suffix (``2026.1.0-rc.1``); a release is the tag ``v`` plus the version. The version is written in the Maven poms (as the project or the parent version), the npm packages, the Helm chart's appVersion, the console's sidebar, -the README badge and the sample jars the browser tests start. This check fails +the badge of every README and the sample jars the browser tests start. This check fails when one of them differs from the root pom. Usage:: @@ -51,6 +51,10 @@ class Carrier: _PACKAGE = r'^( "version": ")(' + _V + r')(")' +# The README and its translations, each with the version badge. +READMES = ("README.md", "README.zh-CN.md", "README.zh-TW.md", "README.ru.md", "README.ko.md", "README.ja.md", + "README.de.md", "README.fr.md", "README.es.md", "README.pt-BR.md", "README.it.md") + # Files other than the poms; each pattern must match at least once. CARRIERS = ( Carrier("core/grantforge-web/package.json", re.compile(_PACKAGE, re.M)), @@ -59,7 +63,7 @@ class Carrier: Carrier("deploy/helm/grantforge/Chart.yaml", re.compile(r'^(appVersion: ")(' + _V + r')(")', re.M)), Carrier("core/grantforge-web/src/layouts/AppLayout.vue", re.compile(r'(v)(' + _V + r')()')), - Carrier("README.md", re.compile(r"(badge/version-)(" + _V + r")(-)")), + *(Carrier(readme, re.compile(r"(badge/version-)(" + _V + r")(-)")) for readme in READMES), Carrier("core/grantforge-web/tests/samples/setup.ts", re.compile(r"(grantforge-sample-[a-z]+-)(" + _V + r")(\.jar)")), ) diff --git a/script/ci/docs.sh b/script/ci/docs.sh index c7083013..14f71d14 100755 --- a/script/ci/docs.sh +++ b/script/ci/docs.sh @@ -6,9 +6,11 @@ # Documentation site (docs/, Next.js and Tailwind CSS) entry points used by CI (and runnable locally). # -# docs.sh install install the locked dependencies -# docs.sh check ESLint, type check, unit tests and the content check (pages, links, images) -# docs.sh build static export to docs/out +# docs.sh install install the locked dependencies +# docs.sh check ESLint, type check, unit tests and the content check (pages, links, images) +# docs.sh translations every page translated completely into every language, and every README into +# every language of README.md (test/translations.test.ts) +# docs.sh build static export to docs/out set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" @@ -16,10 +18,12 @@ cd "${ROOT}/docs" case "${1:-}" in install) pnpm install --frozen-lockfile ;; - check) pnpm lint && pnpm typecheck && pnpm test && pnpm check ;; + # The translation check runs as a step of its own in CI, so its findings are not buried in the unit tests. + check) pnpm lint && pnpm typecheck && pnpm exec vitest run --exclude test/translations.test.ts && pnpm check ;; + translations) pnpm exec vitest run test/translations.test.ts ;; build) pnpm build ;; *) - echo "usage: $0 {install|check|build}" >&2 + echo "usage: $0 {install|check|translations|build}" >&2 exit 2 ;; esac diff --git a/script/ci/tests/test_changed_areas.py b/script/ci/tests/test_changed_areas.py index df1dae0a..7a18dcb5 100644 --- a/script/ci/tests/test_changed_areas.py +++ b/script/ci/tests/test_changed_areas.py @@ -33,6 +33,7 @@ def touched(*paths: str) -> set: class AreasTest(unittest.TestCase): def test_documentation_alone_builds_no_java(self) -> None: self.assertEqual(touched("docs/content/en/plugins/hive.md", "README.md", "README.zh-CN.md"), {"docs"}) + self.assertEqual(touched("README.ja.md"), {"docs"}) self.assertEqual(touched("agents/grantforge-agent-hive/README.md"), set()) self.assertEqual(touched("docs/lib/navigation.ts"), {"docs", "code"}) diff --git a/script/ci/tests/test_check_versions.py b/script/ci/tests/test_check_versions.py index c9d296c9..37dc82cf 100644 --- a/script/ci/tests/test_check_versions.py +++ b/script/ci/tests/test_check_versions.py @@ -54,7 +54,7 @@ "sdk/grantforge-js/package.json": '{{\n "version": "{version}"\n}}\n', "deploy/helm/grantforge/Chart.yaml": 'version: 0.1.0\nappVersion: "{version}"\n', "core/grantforge-web/src/layouts/AppLayout.vue": '© 2026v{version}\n', - "README.md": "![Version](https://img.shields.io/badge/version-{version}-4F46E5)\n", + **{readme: "![Version](https://img.shields.io/badge/version-{version}-4F46E5)\n" for readme in chk.READMES}, "core/grantforge-web/tests/samples/setup.ts": "start('grantforge-sample-shop-{version}.jar')\nstart('grantforge-sample-notes-{version}.jar')\n", }