Every credential provider says whether it still holds a consumer
holds() for postgres, mssql, mongodb, minio, lavinmq, mosquitto, mailu and gitea, so the harness makes again a login the backend lost (hq issue 120). Each checks the mesh's password as the consumer presents it, or compares it read-only, and returns false only when the backend says the credential is absent or wrong; an unreachable backend throws.
This commit is contained in:
@@ -433,6 +433,29 @@ export class GiteaAdmin {
|
||||
GiteaAdmin.fail(`/teams/${teamId}/members/${username}`, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a consumer's user logs in with exactly this password and is still a member of the
|
||||
* package team. Read-only: the password is checked as the consumer presents it, basic auth on the
|
||||
* API, and membership through the admin API. `false` for a refused login or a missing member; any
|
||||
* other answer rejects (novox/hq issue 120).
|
||||
*/
|
||||
async holdsTeamMember(org: string, team: string, username: string, password: string): Promise<boolean> {
|
||||
const me = await fetch(`${this.baseUrl}/api/v1/user`, {
|
||||
headers: { Authorization: "Basic " + Buffer.from(`${username}:${password}`).toString("base64") },
|
||||
});
|
||||
if (me.status === 401 || me.status === 403) return false;
|
||||
if (me.status !== 200) throw new Error(`Gitea GET /user as ${username}: ${me.status}`);
|
||||
const teams = await this.request(`/orgs/${encodeURIComponent(org)}/teams`);
|
||||
if (teams.status === 404) return false;
|
||||
if (teams.status !== 200) GiteaAdmin.fail(`/orgs/${org}/teams`, teams);
|
||||
const found = (teams.body as { id: number; name: string }[]).find((t) => t.name === team);
|
||||
if (!found) return false;
|
||||
const member = await this.request(`/teams/${found.id}/members/${encodeURIComponent(username)}`);
|
||||
if (member.status === 200 || member.status === 204) return true;
|
||||
if (member.status === 404) return false;
|
||||
GiteaAdmin.fail(`/teams/${found.id}/members/${username}`, member);
|
||||
}
|
||||
|
||||
/** Delete a user, purging what they own. A 404 means the mesh already withdrew them — success, not
|
||||
* an error, so a re-run of remove is safe. */
|
||||
async deleteUser(username: string): Promise<void> {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"test": "npm run build && node --test --experimental-strip-types 'test/*.test.ts'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@novox/mesh-sdk": "^0.1.0"
|
||||
"@novox/mesh-sdk": "^0.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
|
||||
@@ -46,4 +46,9 @@ runProvisioner("package-registry", {
|
||||
async remove(p: { as: string }): Promise<void> {
|
||||
await gitea.deleteUser(p.as);
|
||||
},
|
||||
// Asked every minute by the harness: whether the backend still holds this consumer exactly as
|
||||
// the mesh gave it, so a login lost behind the provisioner's back is made again (novox/hq issue 120).
|
||||
async holds(p: Provision): Promise<boolean> {
|
||||
return gitea.holdsTeamMember(ORG, PACKAGE_TEAM, p.as, p.password);
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user