anbaric-cloud-hosting 1.62.0 → 1.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anbaric-cloud-hosting",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.63.0",
|
|
4
4
|
"description": "Anbaric Cloud hosting service: Postgres-backed job persistence and queuing exposed over an HTTP API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "chris@anbaric.ai",
|
|
@@ -18,18 +18,18 @@
|
|
|
18
18
|
"@aws-sdk/client-s3": "^3.1110.0",
|
|
19
19
|
"@aws-sdk/client-secrets-manager": "^3.700.0",
|
|
20
20
|
"@aws-sdk/client-servicediscovery": "^3.1110.0",
|
|
21
|
-
"anbaric-data-store": "^1.
|
|
22
|
-
"anbaric-plugins": "^1.
|
|
23
|
-
"anbaric-tsapi": "^1.
|
|
24
|
-
"anbaric-web": "^1.
|
|
21
|
+
"anbaric-data-store": "^1.63.0",
|
|
22
|
+
"anbaric-plugins": "^1.63.0",
|
|
23
|
+
"anbaric-tsapi": "^1.63.0",
|
|
24
|
+
"anbaric-web": "^1.63.0",
|
|
25
25
|
"esbuild": "^0.28.2",
|
|
26
26
|
"pg": "^8.16.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^26.2.0",
|
|
30
30
|
"@types/pg": "^8.15.0",
|
|
31
|
-
"anbaric-impl-cloud": "^1.
|
|
32
|
-
"anbaric-state-machine": "^1.
|
|
31
|
+
"anbaric-impl-cloud": "^1.63.0",
|
|
32
|
+
"anbaric-state-machine": "^1.63.0",
|
|
33
33
|
"tsx": "^4.20.0",
|
|
34
34
|
"typescript": "^7.0.2"
|
|
35
35
|
},
|
|
@@ -46,6 +46,12 @@ class HttpMembershipService implements MembershipService {
|
|
|
46
46
|
return response.status === 204;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
async remove(userId : string, asking : Inviter) : Promise<boolean> {
|
|
50
|
+
const response = await this.call("DELETE", `/members/${encodeURIComponent(userId)}`, undefined, [404], asking);
|
|
51
|
+
this.roles.delete(userId);
|
|
52
|
+
return response.status === 204;
|
|
53
|
+
}
|
|
54
|
+
|
|
49
55
|
private async call(method : string, path : string, body? : unknown,
|
|
50
56
|
tolerated : Array<number> = [], asking? : Inviter) : Promise<Response> {
|
|
51
57
|
const headers : Record<string, string> = { "x-anbaric-central-key": this.secret };
|
|
@@ -17,6 +17,10 @@ class InMemoryMembershipService implements MembershipService {
|
|
|
17
17
|
return this.members.get(userId);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
async remove(userId : string, _asking : Inviter) : Promise<boolean> {
|
|
21
|
+
return this.members.delete(userId);
|
|
22
|
+
}
|
|
23
|
+
|
|
20
24
|
async invite(email : string, role : TenantRole, invitedBy : Inviter, landingApp? : string) : Promise<PendingInvitation> {
|
|
21
25
|
const token = randomUUID();
|
|
22
26
|
const now = new Date();
|
|
@@ -39,6 +39,9 @@ interface MembershipService {
|
|
|
39
39
|
|
|
40
40
|
revoke(token : string, asking : Inviter) : Promise<boolean>;
|
|
41
41
|
|
|
42
|
+
// Ends someone's membership. False when they were not a member.
|
|
43
|
+
remove(userId : string, asking : Inviter) : Promise<boolean>;
|
|
44
|
+
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
export type { MembershipService, PendingInvitation, Inviter }
|
|
@@ -10,6 +10,7 @@ import {AppProxyHandler} from "./handlers/AppProxyHandler";
|
|
|
10
10
|
import {AppLinkFallbackHandler} from "./handlers/AppLinkFallbackHandler";
|
|
11
11
|
import {AppsHandler} from "./handlers/AppsHandler";
|
|
12
12
|
import {MyAppsHandler} from "./handlers/MyAppsHandler";
|
|
13
|
+
import {MembersHandler} from "./handlers/auth/MembersHandler";
|
|
13
14
|
import {Subdomains} from "../subdomains/Subdomains";
|
|
14
15
|
import {AuditsHandler} from "./handlers/AuditsHandler";
|
|
15
16
|
import {AuthorizeCliHandler} from "./handlers/auth/AuthorizeCliHandler";
|
|
@@ -111,6 +112,7 @@ class HostingServer {
|
|
|
111
112
|
if (entitlements) publicRouter.registerApi("entitlements", new EntitlementsAdminHandler(entitlements));
|
|
112
113
|
if (userDirectory) publicRouter.registerApi("users", new UsersHandler(userDirectory));
|
|
113
114
|
if (memberships) publicRouter.registerApi("invitations", new InvitationsHandler(memberships, entitlements));
|
|
115
|
+
if (memberships) publicRouter.registerApi("members", new MembersHandler(memberships, entitlements));
|
|
114
116
|
const prompts = promptManagerFor && new PromptsHandler(promptManagerFor);
|
|
115
117
|
if (prompts) publicRouter.registerApi("prompts", prompts);
|
|
116
118
|
if (cliAuthorizer) {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {MembershipService} from "../../../auth/MembershipService";
|
|
2
|
+
import {canInvite} from "../../../auth/TenantRole";
|
|
3
|
+
import {EntitlementStore} from "../../../data-store/EntitlementStore";
|
|
4
|
+
import {Request} from "../../Request";
|
|
5
|
+
import {RequestHandler} from "../../RequestHandler";
|
|
6
|
+
|
|
7
|
+
/* The console's side of removing someone from this tenant. Membership is the
|
|
8
|
+
control plane's to end - it is what admits a session at all - and it is
|
|
9
|
+
asked to; what is this platform's own is the entitlements that person held
|
|
10
|
+
here, which go with them so nothing is left granted to somebody who can no
|
|
11
|
+
longer sign in. Their directory entry stays: it is a record of who was here. */
|
|
12
|
+
class MembersHandler implements RequestHandler {
|
|
13
|
+
|
|
14
|
+
constructor(private memberships : MembershipService, private entitlements? : EntitlementStore) {}
|
|
15
|
+
|
|
16
|
+
async handle(request : Request) : Promise<void> {
|
|
17
|
+
if (request.method !== "DELETE" || ! request.id || request.subresource) return request.notFound();
|
|
18
|
+
|
|
19
|
+
if (! canInvite(request.user?.tenantRole)) {
|
|
20
|
+
return request.reply(403, { error: "Your role in this tenant cannot remove members" });
|
|
21
|
+
}
|
|
22
|
+
if (request.user?.id === request.id) {
|
|
23
|
+
return request.reply(400, { error: "You cannot remove yourself from a tenant" });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const asking = request.user ? { id: request.user.id, name: request.user.name } : { id: "system" };
|
|
27
|
+
let removed : boolean;
|
|
28
|
+
try {
|
|
29
|
+
removed = await this.memberships.remove(request.id, asking);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
return request.reply(403, { error: error instanceof Error ? error.message : "Could not remove that member" });
|
|
32
|
+
}
|
|
33
|
+
if (! removed) return request.notFound();
|
|
34
|
+
|
|
35
|
+
for (const grant of await this.entitlements?.listGrants(request.id) ?? []) {
|
|
36
|
+
await this.entitlements?.revoke(grant.id);
|
|
37
|
+
}
|
|
38
|
+
request.reply(204);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export { MembersHandler }
|