Feat #10: Token-Rechte vollständig dokumentiert #11
+24
@@ -485,6 +485,30 @@ Firmen-Gitea nebeneinander.
|
|||||||
Kurzbeschreibung.
|
Kurzbeschreibung.
|
||||||
- **Löschen** trennt nur die Verknüpfung; die Projekte selbst bleiben erhalten.
|
- **Löschen** trennt nur die Verknüpfung; die Projekte selbst bleiben erhalten.
|
||||||
|
|
||||||
|
#### Nötige Token-Rechte
|
||||||
|
|
||||||
|
Nebula liest fast alles nur. Geschrieben wird ausschließlich beim Anlegen und Schließen
|
||||||
|
von Issues (4.6) — wer darauf verzichtet, kommt mit einem rein lesenden Token aus.
|
||||||
|
|
||||||
|
| System | Nur Abgleich | Zusätzlich für die Issue-Anbindung |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| GitHub | *Metadata*, *Contents*, *Issues*, *Pull requests*, *Actions* — je lesend | *Issues: Read and write* |
|
||||||
|
| GitLab | `read_api` | `api` |
|
||||||
|
| Gitea / Forgejo | `read:repository`, `read:issue`, `read:user` | `write:issue` |
|
||||||
|
|
||||||
|
**GitLab kennt keinen schmalen Schreib-Scope.** `api` erlaubt alles, was das Konto darf,
|
||||||
|
Code eingeschlossen. Wem das zu weit geht, bleibt bei `read_api`: der Abgleich läuft
|
||||||
|
vollständig, geschlossene Issues werden weiterhin erkannt — nur schreibt Nebula dann
|
||||||
|
nicht zurück.
|
||||||
|
|
||||||
|
**Nicht verlangt** werden Schreibrechte auf Code, Adminrechte und Rechte zum Anlegen von
|
||||||
|
Webhooks. Der Haken wird von Hand im Git-System eingetragen (4.7); das erspart dem Token
|
||||||
|
genau die Administrationsrechte, die er sonst bräuchte.
|
||||||
|
|
||||||
|
Ein fehlendes Recht endet in einem `403` mit „Token hat nicht die noetigen Scopes",
|
||||||
|
sichtbar am Repository auf der Projektseite. Klein anfangen und gezielt nachlegen ist
|
||||||
|
deshalb ohne Risiko.
|
||||||
|
|
||||||
### 4.3 Was beim Sync geholt wird
|
### 4.3 Was beim Sync geholt wird
|
||||||
|
|
||||||
| Bereich | Details |
|
| Bereich | Details |
|
||||||
|
|||||||
@@ -173,14 +173,8 @@ Nebula auffindbare Credentials nutzt.
|
|||||||
|
|
||||||
## Git-Verbindung einrichten
|
## Git-Verbindung einrichten
|
||||||
|
|
||||||
1. Im Git-System einen **Personal Access Token** mit Leserechten erzeugen:
|
1. Im Git-System einen **Personal Access Token** erzeugen — mit den Rechten aus dem
|
||||||
|
Abschnitt [Welche Rechte der Token braucht](#welche-rechte-der-token-braucht).
|
||||||
| System | Scopes |
|
|
||||||
| --- | --- |
|
|
||||||
| GitHub | Fine-grained Token: *Contents: Read*, *Metadata: Read*, *Issues: Read*, *Pull requests: Read* |
|
|
||||||
| GitLab | `read_api` (alternativ `read_repository`) |
|
|
||||||
| Gitea / Forgejo | `repository:read`, `issue:read`, `user:read` |
|
|
||||||
|
|
||||||
2. In Nebula unter **Git-Verbindungen → Verbindung hinzufügen** eintragen:
|
2. In Nebula unter **Git-Verbindungen → Verbindung hinzufügen** eintragen:
|
||||||
System wählen, Basis-URL (z. B. `https://git.meine-firma.de`, ohne `/api`) und Token.
|
System wählen, Basis-URL (z. B. `https://git.meine-firma.de`, ohne `/api`) und Token.
|
||||||
3. Mit **Testen** prüfen — Nebula meldet den erkannten Benutzer zurück.
|
3. Mit **Testen** prüfen — Nebula meldet den erkannten Benutzer zurück.
|
||||||
@@ -192,6 +186,47 @@ Nebula auffindbare Credentials nutzt.
|
|||||||
Der Snapshot bleibt in der Datenbank, das Dashboard funktioniert also auch offline.
|
Der Snapshot bleibt in der Datenbank, das Dashboard funktioniert also auch offline.
|
||||||
Ohne Token gehen nur öffentliche Repositories, mit strengem Rate-Limit.
|
Ohne Token gehen nur öffentliche Repositories, mit strengem Rate-Limit.
|
||||||
|
|
||||||
|
### Welche Rechte der Token braucht
|
||||||
|
|
||||||
|
Nebula liest fast alles nur — geschrieben wird ausschließlich, wenn du eine Aufgabe als
|
||||||
|
Issue anlegst oder abhakst. Deshalb lohnt es, zwei Fälle zu unterscheiden:
|
||||||
|
|
||||||
|
**Fall A — nur Abgleich.** Branches, Tags, Commits, Releases, CI-Zustand, README,
|
||||||
|
Heatmap, offene Issues und PRs anzeigen. Rein lesend.
|
||||||
|
|
||||||
|
**Fall B — zusätzlich die Issue-Anbindung.** Eine Aufgabe als Issue anlegen; wird das
|
||||||
|
Issue geschlossen, hakt Nebula die Aufgabe ab und schließt sie umgekehrt mit.
|
||||||
|
|
||||||
|
| System | Fall A (nur lesen) | Fall B zusätzlich |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **GitHub** | Fine-grained Token: *Metadata: Read*, *Contents: Read*, *Issues: Read*, *Pull requests: Read*, *Actions: Read* | *Issues: **Read and write*** |
|
||||||
|
| **GitLab** | `read_api` | `api` — **es gibt nichts dazwischen** |
|
||||||
|
| **Gitea / Forgejo** | `read:repository`, `read:issue`, `read:user` | `write:issue` |
|
||||||
|
|
||||||
|
Wozu die einzelnen Rechte gut sind:
|
||||||
|
|
||||||
|
| Recht | Wofür |
|
||||||
|
| --- | --- |
|
||||||
|
| Repository lesen (`Contents`, `read:repository`) | Branches, Tags, Commits, Releases, Sprachen, README, Commit-Heatmap |
|
||||||
|
| Issues lesen | Offene Issues auf der Projektseite, „Issues als Aufgaben übernehmen" |
|
||||||
|
| Pull Requests lesen | Offene PRs auf der Projektseite |
|
||||||
|
| Actions lesen (nur GitHub) | CI-Zustand des Default-Branch. Bei GitLab kommt er aus den Pipelines (`read_api`), bei Gitea aus dem Commit-Status (`read:repository`) |
|
||||||
|
| Benutzer lesen | Nur für den Verbindungstest — Nebula meldet zurück, als wer der Token gilt |
|
||||||
|
| Issues schreiben | Aufgabe als Issue anlegen, schließen, wieder öffnen |
|
||||||
|
|
||||||
|
> **GitLab ist der unangenehme Fall.** Es kennt keinen schmalen Schreib-Scope für Issues:
|
||||||
|
> `api` erlaubt alles, was dein Konto darf — auch Code ändern. Wenn dir das zu weit geht,
|
||||||
|
> nimm `read_api` und verzichte auf die Issue-Anbindung. Der Abgleich funktioniert
|
||||||
|
> vollständig, und geschlossene Issues werden trotzdem erkannt — nur eben nur gelesen.
|
||||||
|
|
||||||
|
**Was Nebula *nicht* braucht:** Schreibrechte auf Code, Adminrechte, Rechte zum Anlegen
|
||||||
|
von Webhooks. Den Haken für Webhooks trägst du selbst im Git-System ein — genau deshalb
|
||||||
|
verlangt Nebula dafür kein zusätzliches Recht.
|
||||||
|
|
||||||
|
**Im Zweifel klein anfangen.** Ein fehlendes Recht führt zu einem `403` mit dem Hinweis
|
||||||
|
„Token hat nicht die noetigen Scopes"; die Meldung steht am Repository auf der
|
||||||
|
Projektseite. Dann gezielt nachlegen, statt vorsorglich alles zu vergeben.
|
||||||
|
|
||||||
### Automatisch statt auf Knopfdruck
|
### Automatisch statt auf Knopfdruck
|
||||||
|
|
||||||
Unter **Admin-Einstellungen → Automatischer Abgleich** lässt sich der Sync in den
|
Unter **Admin-Einstellungen → Automatischer Abgleich** lässt sich der Sync in den
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { PROVIDER_META, guessProvider } from "./meta";
|
||||||
|
|
||||||
|
const PROVIDER = ["GITHUB", "GITLAB", "GITEA"] as const;
|
||||||
|
|
||||||
|
describe("PROVIDER_META", () => {
|
||||||
|
it("kennt jedes unterstuetzte System", () => {
|
||||||
|
for (const provider of PROVIDER) {
|
||||||
|
expect(PROVIDER_META[provider], provider).toBeDefined();
|
||||||
|
}
|
||||||
|
// Und nichts darueber hinaus – ein Eintrag ohne Gegenstueck im Schema
|
||||||
|
// waere ein Ueberbleibsel.
|
||||||
|
expect(Object.keys(PROVIDER_META).sort()).toEqual([...PROVIDER].sort());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("hat zu jedem System Beschriftung, Basis-URL und Dokuverweis", () => {
|
||||||
|
for (const provider of PROVIDER) {
|
||||||
|
const meta = PROVIDER_META[provider];
|
||||||
|
expect(meta.label.length, provider).toBeGreaterThan(0);
|
||||||
|
expect(meta.defaultBaseUrl, provider).toMatch(/^https:\/\//);
|
||||||
|
expect(meta.docsUrl, provider).toMatch(/^https:\/\//);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Der eigentliche Waechter.
|
||||||
|
*
|
||||||
|
* Der Hinweis am Eingabefeld war eine Zeit lang rein lesend formuliert,
|
||||||
|
* obwohl Nebula seit v1.7.0 Issues anlegt und schliesst – wer sich daran
|
||||||
|
* hielt, bekam einen 403. Jeder Provider in GitClient muss createIssue und
|
||||||
|
* setIssueState koennen; folglich muss auch jeder Hinweis sagen, was das
|
||||||
|
* an Rechten kostet.
|
||||||
|
*/
|
||||||
|
it("sagt bei jedem System etwas ueber die Rechte fuer Issues", () => {
|
||||||
|
for (const provider of PROVIDER) {
|
||||||
|
expect(PROVIDER_META[provider].tokenHelp, provider).toMatch(/issue/i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("nennt einen konkreten Scope statt nur 'Leserechte'", () => {
|
||||||
|
// GitLab nennt Scopes in Anfuehrungszeichen, GitHub und Gitea
|
||||||
|
// Berechtigungsnamen – gemeinsam ist, dass etwas Nachschlagbares dasteht.
|
||||||
|
expect(PROVIDER_META.GITLAB.tokenHelp).toMatch(/read_api/);
|
||||||
|
expect(PROVIDER_META.GITEA.tokenHelp).toMatch(/read:repository/);
|
||||||
|
expect(PROVIDER_META.GITHUB.tokenHelp).toMatch(/Metadata/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("guessProvider", () => {
|
||||||
|
it("erkennt die oeffentlichen Instanzen am Hostnamen", () => {
|
||||||
|
expect(guessProvider("github.com")).toBe("GITHUB");
|
||||||
|
expect(guessProvider("gitlab.com")).toBe("GITLAB");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("faellt bei allem anderen auf Gitea zurueck", () => {
|
||||||
|
// Selbst gehostet ist hier der haeufigste Fall, und Gitea die
|
||||||
|
// wahrscheinlichste Wahl dafuer.
|
||||||
|
expect(guessProvider("git.wilde-server.de")).toBe("GITEA");
|
||||||
|
});
|
||||||
|
});
|
||||||
+8
-3
@@ -12,19 +12,24 @@ export const PROVIDER_META: Record<
|
|||||||
label: "GitHub",
|
label: "GitHub",
|
||||||
defaultBaseUrl: "https://github.com",
|
defaultBaseUrl: "https://github.com",
|
||||||
tokenHelp:
|
tokenHelp:
|
||||||
"Fine-grained Token mit Lesezugriff auf Contents, Metadata, Issues und Pull Requests.",
|
"Fine-grained Token: Metadata, Contents und Pull requests lesen, Issues lesen. " +
|
||||||
|
"Nur wenn Aufgaben als Issue angelegt werden sollen, Issues zusaetzlich schreiben.",
|
||||||
docsUrl: "https://github.com/settings/tokens",
|
docsUrl: "https://github.com/settings/tokens",
|
||||||
},
|
},
|
||||||
GITLAB: {
|
GITLAB: {
|
||||||
label: "GitLab",
|
label: "GitLab",
|
||||||
defaultBaseUrl: "https://gitlab.com",
|
defaultBaseUrl: "https://gitlab.com",
|
||||||
tokenHelp: "Personal Access Token mit Scope 'read_api' (bzw. 'read_repository').",
|
tokenHelp:
|
||||||
|
"Scope 'read_api' genuegt fuer den Abgleich. Aufgaben als Issue anzulegen verlangt " +
|
||||||
|
"'api' – GitLab kennt keinen schmaleren Schreibzugriff.",
|
||||||
docsUrl: "https://gitlab.com/-/user_settings/personal_access_tokens",
|
docsUrl: "https://gitlab.com/-/user_settings/personal_access_tokens",
|
||||||
},
|
},
|
||||||
GITEA: {
|
GITEA: {
|
||||||
label: "Gitea / Forgejo",
|
label: "Gitea / Forgejo",
|
||||||
defaultBaseUrl: "https://git.example.com",
|
defaultBaseUrl: "https://git.example.com",
|
||||||
tokenHelp: "Access Token mit Leserechten auf repository, issue und user.",
|
tokenHelp:
|
||||||
|
"read:repository, read:issue und read:user. Nur wenn Aufgaben als Issue angelegt " +
|
||||||
|
"werden sollen, zusaetzlich write:issue.",
|
||||||
docsUrl: "https://docs.gitea.com/development/api-usage",
|
docsUrl: "https://docs.gitea.com/development/api-usage",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user