@peers-app/peers-sdk 0.18.4 → 0.18.5
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/dist/data/package-version-permissions.d.ts +4 -1
- package/dist/data/package-version-permissions.js +7 -3
- package/dist/data/package-version-permissions.test.d.ts +1 -0
- package/dist/data/package-version-permissions.test.js +107 -0
- package/dist/data/package-versions.js +2 -9
- package/dist/data/package-versions.test.d.ts +1 -0
- package/dist/data/package-versions.test.js +227 -0
- package/dist/data/packages.js +2 -9
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { GroupMemberRole } from "./group-permissions";
|
|
1
2
|
import type { IPackageVersion } from "./package-versions";
|
|
2
3
|
/**
|
|
3
4
|
* Verifies that a package version update signature is valid.
|
|
@@ -6,6 +7,8 @@ import type { IPackageVersion } from "./package-versions";
|
|
|
6
7
|
* Beta and stable versions require Admin role.
|
|
7
8
|
*
|
|
8
9
|
* @param packageVersion The package version record to verify
|
|
10
|
+
* @param groupId The group context to check roles against
|
|
11
|
+
* @param signerRole Optional pre-resolved role (skips `getUserRoleFromPublicKey` lookup; useful in tests)
|
|
9
12
|
* @throws Error if signature is invalid or unauthorized
|
|
10
13
|
*/
|
|
11
|
-
export declare function verifyPackageVersionSignature(packageVersion: IPackageVersion, groupId: string): Promise<void>;
|
|
14
|
+
export declare function verifyPackageVersionSignature(packageVersion: IPackageVersion, groupId: string, signerRole?: GroupMemberRole): Promise<void>;
|
|
@@ -10,12 +10,16 @@ const group_permissions_1 = require("./group-permissions");
|
|
|
10
10
|
* Beta and stable versions require Admin role.
|
|
11
11
|
*
|
|
12
12
|
* @param packageVersion The package version record to verify
|
|
13
|
+
* @param groupId The group context to check roles against
|
|
14
|
+
* @param signerRole Optional pre-resolved role (skips `getUserRoleFromPublicKey` lookup; useful in tests)
|
|
13
15
|
* @throws Error if signature is invalid or unauthorized
|
|
14
16
|
*/
|
|
15
|
-
async function verifyPackageVersionSignature(packageVersion, groupId) {
|
|
17
|
+
async function verifyPackageVersionSignature(packageVersion, groupId, signerRole) {
|
|
16
18
|
(0, keys_1.verifyObjectSignature)(packageVersion);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
if (signerRole === undefined) {
|
|
20
|
+
const signerPublicKey = (0, keys_1.getPublicKeyFromObjectSignature)(packageVersion) ?? "";
|
|
21
|
+
signerRole = await (0, group_permissions_1.getUserRoleFromPublicKey)(groupId, signerPublicKey);
|
|
22
|
+
}
|
|
19
23
|
const isDevVersion = packageVersion.versionTag === "dev";
|
|
20
24
|
const requiredRole = isDevVersion ? group_permissions_1.GroupMemberRole.Writer : group_permissions_1.GroupMemberRole.Admin;
|
|
21
25
|
if (signerRole < requiredRole) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const keys_1 = require("../keys");
|
|
4
|
+
const utils_1 = require("../utils");
|
|
5
|
+
const group_member_roles_1 = require("./group-member-roles");
|
|
6
|
+
const package_version_permissions_1 = require("./package-version-permissions");
|
|
7
|
+
describe("Package Version Permissions", () => {
|
|
8
|
+
const writerKeys = (0, keys_1.newKeys)();
|
|
9
|
+
const adminKeys = (0, keys_1.newKeys)();
|
|
10
|
+
function makePV(overrides = {}) {
|
|
11
|
+
return {
|
|
12
|
+
packageVersionId: (0, utils_1.newid)(),
|
|
13
|
+
packageId: (0, utils_1.newid)(),
|
|
14
|
+
version: "1.0.0",
|
|
15
|
+
versionTag: "dev",
|
|
16
|
+
packageVersionHash: "testhash",
|
|
17
|
+
packageBundleFileId: (0, utils_1.newid)(),
|
|
18
|
+
packageBundleFileHash: "bundlehash",
|
|
19
|
+
signature: "",
|
|
20
|
+
createdBy: (0, utils_1.newid)(),
|
|
21
|
+
createdAt: new Date().toISOString(),
|
|
22
|
+
...overrides,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
describe("dev versions", () => {
|
|
26
|
+
it("allows Writer to sign a dev version", async () => {
|
|
27
|
+
const pv = (0, keys_1.addSignatureToObject)(makePV({ versionTag: "dev" }), writerKeys.secretKey);
|
|
28
|
+
await expect((0, package_version_permissions_1.verifyPackageVersionSignature)(pv, "test-group", group_member_roles_1.GroupMemberRole.Writer)).resolves.toBeUndefined();
|
|
29
|
+
});
|
|
30
|
+
it("allows Admin to sign a dev version", async () => {
|
|
31
|
+
const pv = (0, keys_1.addSignatureToObject)(makePV({ versionTag: "dev" }), adminKeys.secretKey);
|
|
32
|
+
await expect((0, package_version_permissions_1.verifyPackageVersionSignature)(pv, "test-group", group_member_roles_1.GroupMemberRole.Admin)).resolves.toBeUndefined();
|
|
33
|
+
});
|
|
34
|
+
it("rejects Reader signing a dev version", async () => {
|
|
35
|
+
const pv = (0, keys_1.addSignatureToObject)(makePV({ versionTag: "dev" }), writerKeys.secretKey);
|
|
36
|
+
await expect((0, package_version_permissions_1.verifyPackageVersionSignature)(pv, "test-group", group_member_roles_1.GroupMemberRole.Reader)).rejects.toThrow("Only group writers or above");
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe("beta versions", () => {
|
|
40
|
+
it("allows Admin to sign a beta version", async () => {
|
|
41
|
+
const pv = (0, keys_1.addSignatureToObject)(makePV({ versionTag: "beta" }), adminKeys.secretKey);
|
|
42
|
+
await expect((0, package_version_permissions_1.verifyPackageVersionSignature)(pv, "test-group", group_member_roles_1.GroupMemberRole.Admin)).resolves.toBeUndefined();
|
|
43
|
+
});
|
|
44
|
+
it("allows Owner to sign a beta version", async () => {
|
|
45
|
+
const pv = (0, keys_1.addSignatureToObject)(makePV({ versionTag: "beta" }), adminKeys.secretKey);
|
|
46
|
+
await expect((0, package_version_permissions_1.verifyPackageVersionSignature)(pv, "test-group", group_member_roles_1.GroupMemberRole.Owner)).resolves.toBeUndefined();
|
|
47
|
+
});
|
|
48
|
+
it("rejects Writer signing a beta version", async () => {
|
|
49
|
+
const pv = (0, keys_1.addSignatureToObject)(makePV({ versionTag: "beta" }), writerKeys.secretKey);
|
|
50
|
+
await expect((0, package_version_permissions_1.verifyPackageVersionSignature)(pv, "test-group", group_member_roles_1.GroupMemberRole.Writer)).rejects.toThrow("Only group admins can create or update beta/stable");
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe("stable versions", () => {
|
|
54
|
+
it("allows Admin to sign a stable version", async () => {
|
|
55
|
+
const pv = (0, keys_1.addSignatureToObject)(makePV({ versionTag: "stable" }), adminKeys.secretKey);
|
|
56
|
+
await expect((0, package_version_permissions_1.verifyPackageVersionSignature)(pv, "test-group", group_member_roles_1.GroupMemberRole.Admin)).resolves.toBeUndefined();
|
|
57
|
+
});
|
|
58
|
+
it("rejects Writer signing a stable version", async () => {
|
|
59
|
+
const pv = (0, keys_1.addSignatureToObject)(makePV({ versionTag: "stable" }), writerKeys.secretKey);
|
|
60
|
+
await expect((0, package_version_permissions_1.verifyPackageVersionSignature)(pv, "test-group", group_member_roles_1.GroupMemberRole.Writer)).rejects.toThrow("Only group admins can create or update beta/stable");
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe("promotion scenarios (dev → beta → stable)", () => {
|
|
64
|
+
it("rejects Writer promoting dev to beta", async () => {
|
|
65
|
+
const devPV = makePV({ versionTag: "dev" });
|
|
66
|
+
const promoted = { ...devPV, versionTag: "beta" };
|
|
67
|
+
const signed = (0, keys_1.addSignatureToObject)(promoted, writerKeys.secretKey);
|
|
68
|
+
await expect((0, package_version_permissions_1.verifyPackageVersionSignature)(signed, "test-group", group_member_roles_1.GroupMemberRole.Writer)).rejects.toThrow("Only group admins can create or update beta/stable");
|
|
69
|
+
});
|
|
70
|
+
it("rejects Writer promoting dev to stable", async () => {
|
|
71
|
+
const devPV = makePV({ versionTag: "dev" });
|
|
72
|
+
const promoted = { ...devPV, versionTag: "stable" };
|
|
73
|
+
const signed = (0, keys_1.addSignatureToObject)(promoted, writerKeys.secretKey);
|
|
74
|
+
await expect((0, package_version_permissions_1.verifyPackageVersionSignature)(signed, "test-group", group_member_roles_1.GroupMemberRole.Writer)).rejects.toThrow("Only group admins can create or update beta/stable");
|
|
75
|
+
});
|
|
76
|
+
it("rejects Writer promoting beta to stable", async () => {
|
|
77
|
+
const betaPV = makePV({ versionTag: "beta" });
|
|
78
|
+
const promoted = { ...betaPV, versionTag: "stable" };
|
|
79
|
+
const signed = (0, keys_1.addSignatureToObject)(promoted, writerKeys.secretKey);
|
|
80
|
+
await expect((0, package_version_permissions_1.verifyPackageVersionSignature)(signed, "test-group", group_member_roles_1.GroupMemberRole.Writer)).rejects.toThrow("Only group admins can create or update beta/stable");
|
|
81
|
+
});
|
|
82
|
+
it("allows Admin promoting dev to beta", async () => {
|
|
83
|
+
const devPV = makePV({ versionTag: "dev" });
|
|
84
|
+
const promoted = { ...devPV, versionTag: "beta" };
|
|
85
|
+
const signed = (0, keys_1.addSignatureToObject)(promoted, adminKeys.secretKey);
|
|
86
|
+
await expect((0, package_version_permissions_1.verifyPackageVersionSignature)(signed, "test-group", group_member_roles_1.GroupMemberRole.Admin)).resolves.toBeUndefined();
|
|
87
|
+
});
|
|
88
|
+
it("allows Admin promoting beta to stable", async () => {
|
|
89
|
+
const betaPV = makePV({ versionTag: "beta" });
|
|
90
|
+
const promoted = { ...betaPV, versionTag: "stable" };
|
|
91
|
+
const signed = (0, keys_1.addSignatureToObject)(promoted, adminKeys.secretKey);
|
|
92
|
+
await expect((0, package_version_permissions_1.verifyPackageVersionSignature)(signed, "test-group", group_member_roles_1.GroupMemberRole.Admin)).resolves.toBeUndefined();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
describe("signature tampering", () => {
|
|
96
|
+
it("rejects a tampered package version", async () => {
|
|
97
|
+
const pv = (0, keys_1.addSignatureToObject)(makePV({ versionTag: "dev" }), writerKeys.secretKey);
|
|
98
|
+
pv.version = "9.9.9";
|
|
99
|
+
await expect((0, package_version_permissions_1.verifyPackageVersionSignature)(pv, "test-group", group_member_roles_1.GroupMemberRole.Admin)).rejects.toThrow();
|
|
100
|
+
});
|
|
101
|
+
it("rejects a version with tag changed after signing", async () => {
|
|
102
|
+
const pv = (0, keys_1.addSignatureToObject)(makePV({ versionTag: "dev" }), writerKeys.secretKey);
|
|
103
|
+
pv.versionTag = "stable";
|
|
104
|
+
await expect((0, package_version_permissions_1.verifyPackageVersionSignature)(pv, "test-group", group_member_roles_1.GroupMemberRole.Admin)).rejects.toThrow();
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
});
|
|
@@ -113,14 +113,7 @@ let PackageVersionsTable = (() => {
|
|
|
113
113
|
// users can do whatever they want to package versions in their personal space
|
|
114
114
|
return super.save(packageVersion, opts);
|
|
115
115
|
}
|
|
116
|
-
|
|
117
|
-
await (0, package_version_permissions_1.verifyPackageVersionSignature)(packageVersion, this.groupId);
|
|
118
|
-
}
|
|
119
|
-
catch (err) {
|
|
120
|
-
throw new Error("Package version verification failed. Did you mean to call `signAndSave`?", {
|
|
121
|
-
cause: err,
|
|
122
|
-
});
|
|
123
|
-
}
|
|
116
|
+
await (0, package_version_permissions_1.verifyPackageVersionSignature)(packageVersion, this.groupId);
|
|
124
117
|
return super.save(packageVersion, opts);
|
|
125
118
|
}
|
|
126
119
|
async signAndSave(packageVersion, opts) {
|
|
@@ -128,7 +121,7 @@ let PackageVersionsTable = (() => {
|
|
|
128
121
|
throw new Error("Package version signing is not enabled. Call PackageVersionsTable.enablePackageVersionSigning(fn) to enable it.");
|
|
129
122
|
}
|
|
130
123
|
packageVersion = await PackageVersionsTable.addSignatureToPackageVersion(packageVersion);
|
|
131
|
-
return
|
|
124
|
+
return this.save(packageVersion, opts);
|
|
132
125
|
}
|
|
133
126
|
static addSignatureToPackageVersion = undefined;
|
|
134
127
|
static enablePackageVersionSigning(fn) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const SQLiteDB = require("better-sqlite3");
|
|
4
|
+
const user_context_1 = require("../context/user-context");
|
|
5
|
+
const user_context_singleton_1 = require("../context/user-context-singleton");
|
|
6
|
+
const keys_1 = require("../keys");
|
|
7
|
+
const utils_1 = require("../utils");
|
|
8
|
+
const group_member_roles_1 = require("./group-member-roles");
|
|
9
|
+
const group_members_1 = require("./group-members");
|
|
10
|
+
const groups_1 = require("./groups");
|
|
11
|
+
const sql_data_source_1 = require("./orm/sql.data-source");
|
|
12
|
+
const package_versions_1 = require("./package-versions");
|
|
13
|
+
const users_1 = require("./users");
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// In-memory SQLite harness (same pattern as sql.data-source.test.ts)
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
class DBHarness {
|
|
18
|
+
_db = null;
|
|
19
|
+
get db() {
|
|
20
|
+
if (!this._db) {
|
|
21
|
+
this._db = new SQLiteDB(":memory:");
|
|
22
|
+
this._db.pragma("journal_mode = WAL");
|
|
23
|
+
}
|
|
24
|
+
return this._db;
|
|
25
|
+
}
|
|
26
|
+
async get(sql, params = []) {
|
|
27
|
+
return this.db.prepare(sql).get(params);
|
|
28
|
+
}
|
|
29
|
+
async all(sql, params = []) {
|
|
30
|
+
return this.db.prepare(sql).all(params);
|
|
31
|
+
}
|
|
32
|
+
async exec(sql, params = []) {
|
|
33
|
+
const result = this.db.prepare(sql).run(params);
|
|
34
|
+
return { changes: result.changes };
|
|
35
|
+
}
|
|
36
|
+
async close() {
|
|
37
|
+
this._db?.close();
|
|
38
|
+
this._db = null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Test fixtures
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
const writerKeys = (0, keys_1.newKeys)();
|
|
45
|
+
const adminKeys = (0, keys_1.newKeys)();
|
|
46
|
+
const testGroupId = (0, utils_1.newid)();
|
|
47
|
+
const writerUserId = (0, utils_1.newid)();
|
|
48
|
+
const adminUserId = (0, utils_1.newid)();
|
|
49
|
+
function makePV(overrides = {}) {
|
|
50
|
+
return {
|
|
51
|
+
packageVersionId: (0, utils_1.newid)(),
|
|
52
|
+
packageId: (0, utils_1.newid)(),
|
|
53
|
+
version: "1.0.0",
|
|
54
|
+
versionTag: "dev",
|
|
55
|
+
packageVersionHash: "testhash",
|
|
56
|
+
packageBundleFileId: (0, utils_1.newid)(),
|
|
57
|
+
packageBundleFileHash: "bundlehash",
|
|
58
|
+
signature: "",
|
|
59
|
+
createdBy: writerUserId,
|
|
60
|
+
createdAt: new Date().toISOString(),
|
|
61
|
+
...overrides,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// Setup: create a real ephemeral UserContext with Users + GroupMembers seeded
|
|
66
|
+
// so that getUserRoleFromPublicKey resolves Writer vs Admin from real data.
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
let userContext;
|
|
69
|
+
beforeAll(async () => {
|
|
70
|
+
const db = new DBHarness();
|
|
71
|
+
const dataSourceFactory = (metaData, schema) => {
|
|
72
|
+
return new sql_data_source_1.SQLDataSource(db, metaData, schema);
|
|
73
|
+
};
|
|
74
|
+
userContext = new user_context_1.UserContext(writerUserId, dataSourceFactory, true);
|
|
75
|
+
await userContext.loadingPromise;
|
|
76
|
+
userContext.deviceId((0, utils_1.newid)());
|
|
77
|
+
(0, user_context_singleton_1.setUserContext)(userContext);
|
|
78
|
+
const dc = userContext.userDataContext;
|
|
79
|
+
// Enable passthrough so seeding skips signature checks on Groups/GroupMembers/Users
|
|
80
|
+
groups_1.GroupsTable.isPassthrough = true;
|
|
81
|
+
group_members_1.GroupMembersTable.isPassthrough = true;
|
|
82
|
+
users_1.UsersTable.isPassthrough = true;
|
|
83
|
+
// Seed the writer user
|
|
84
|
+
const { Users } = await Promise.resolve().then(() => require("./users"));
|
|
85
|
+
const writerUser = {
|
|
86
|
+
userId: writerUserId,
|
|
87
|
+
name: "Writer User",
|
|
88
|
+
publicKey: writerKeys.publicKey,
|
|
89
|
+
publicBoxKey: "",
|
|
90
|
+
signature: "",
|
|
91
|
+
};
|
|
92
|
+
await Users(dc).save(writerUser);
|
|
93
|
+
// Seed the admin user
|
|
94
|
+
const adminUser = {
|
|
95
|
+
userId: adminUserId,
|
|
96
|
+
name: "Admin User",
|
|
97
|
+
publicKey: adminKeys.publicKey,
|
|
98
|
+
publicBoxKey: "",
|
|
99
|
+
signature: "",
|
|
100
|
+
};
|
|
101
|
+
await Users(dc).save(adminUser);
|
|
102
|
+
// Seed a group so the group lookup finds real data (not "group not found → Founder")
|
|
103
|
+
const { Groups } = await Promise.resolve().then(() => require("./groups"));
|
|
104
|
+
await Groups(dc).save({
|
|
105
|
+
groupId: testGroupId,
|
|
106
|
+
name: "Test Group",
|
|
107
|
+
description: "",
|
|
108
|
+
founderUserId: (0, utils_1.newid)(), // different from writer/admin so nobody is auto-Founder
|
|
109
|
+
signature: "",
|
|
110
|
+
publicKey: "",
|
|
111
|
+
publicBoxKey: "",
|
|
112
|
+
});
|
|
113
|
+
// Seed group memberships with real roles
|
|
114
|
+
const { GroupMembers } = await Promise.resolve().then(() => require("./group-members"));
|
|
115
|
+
const writerMembership = {
|
|
116
|
+
groupMemberId: (0, utils_1.newid)(),
|
|
117
|
+
groupId: testGroupId,
|
|
118
|
+
userId: writerUserId,
|
|
119
|
+
role: group_member_roles_1.GroupMemberRole.Writer,
|
|
120
|
+
signature: "",
|
|
121
|
+
};
|
|
122
|
+
await GroupMembers(dc).save(writerMembership);
|
|
123
|
+
const adminMembership = {
|
|
124
|
+
groupMemberId: (0, utils_1.newid)(),
|
|
125
|
+
groupId: testGroupId,
|
|
126
|
+
userId: adminUserId,
|
|
127
|
+
role: group_member_roles_1.GroupMemberRole.Admin,
|
|
128
|
+
signature: "",
|
|
129
|
+
};
|
|
130
|
+
await GroupMembers(dc).save(adminMembership);
|
|
131
|
+
// Disable passthrough so the real permission checks apply during tests
|
|
132
|
+
groups_1.GroupsTable.isPassthrough = false;
|
|
133
|
+
group_members_1.GroupMembersTable.isPassthrough = false;
|
|
134
|
+
users_1.UsersTable.isPassthrough = false;
|
|
135
|
+
// Enable real signing
|
|
136
|
+
package_versions_1.PackageVersionsTable.enablePackageVersionSigning((pv) => (0, keys_1.addSignatureToObject)(pv, activeSecretKey));
|
|
137
|
+
});
|
|
138
|
+
let activeSecretKey = writerKeys.secretKey;
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
// Tests
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
describe("PackageVersionsTable.signAndSave — promotion permission enforcement", () => {
|
|
143
|
+
// These tests exercise the real signAndSave → save → verifyPackageVersionSignature
|
|
144
|
+
// chain against a real in-memory database with real group membership data.
|
|
145
|
+
//
|
|
146
|
+
// THE BUG: signAndSave previously called super.save() which skipped the
|
|
147
|
+
// overridden save() and its verifyPackageVersionSignature check. A Writer
|
|
148
|
+
// could promote dev → beta/stable unchallenged.
|
|
149
|
+
//
|
|
150
|
+
// THE FIX: signAndSave now calls this.save(), so the permission check fires.
|
|
151
|
+
// Helper: get the PackageVersions table in the test group context
|
|
152
|
+
function pvTable() {
|
|
153
|
+
const dc = userContext.getDataContext(testGroupId);
|
|
154
|
+
return (0, package_versions_1.PackageVersions)(dc);
|
|
155
|
+
}
|
|
156
|
+
describe("Writer role", () => {
|
|
157
|
+
beforeEach(() => {
|
|
158
|
+
activeSecretKey = writerKeys.secretKey;
|
|
159
|
+
});
|
|
160
|
+
it("can signAndSave a dev version", async () => {
|
|
161
|
+
const pv = makePV({ versionTag: "dev" });
|
|
162
|
+
const saved = await pvTable().signAndSave(pv);
|
|
163
|
+
expect(saved.versionTag).toBe("dev");
|
|
164
|
+
expect(saved.signature).toBeTruthy();
|
|
165
|
+
});
|
|
166
|
+
it("is blocked from signAndSave with versionTag=beta", async () => {
|
|
167
|
+
const pv = makePV({ versionTag: "beta" });
|
|
168
|
+
// Before the fix: this would PASS (super.save skipped the check)
|
|
169
|
+
// After the fix: this correctly REJECTS
|
|
170
|
+
await expect(pvTable().signAndSave(pv)).rejects.toThrow("Only group admins can create or update beta/stable package versions");
|
|
171
|
+
});
|
|
172
|
+
it("is blocked from signAndSave with versionTag=stable", async () => {
|
|
173
|
+
const pv = makePV({ versionTag: "stable" });
|
|
174
|
+
await expect(pvTable().signAndSave(pv)).rejects.toThrow("Only group admins can create or update beta/stable package versions");
|
|
175
|
+
});
|
|
176
|
+
it("is blocked from promoting an existing dev version to beta", async () => {
|
|
177
|
+
const pv = makePV({ versionTag: "dev" });
|
|
178
|
+
const saved = await pvTable().signAndSave(pv);
|
|
179
|
+
const promoted = { ...saved, versionTag: "beta" };
|
|
180
|
+
await expect(pvTable().signAndSave(promoted)).rejects.toThrow("Only group admins can create or update beta/stable package versions");
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
describe("Admin role", () => {
|
|
184
|
+
beforeEach(() => {
|
|
185
|
+
activeSecretKey = adminKeys.secretKey;
|
|
186
|
+
});
|
|
187
|
+
it("can signAndSave a dev version", async () => {
|
|
188
|
+
const pv = makePV({ versionTag: "dev" });
|
|
189
|
+
const saved = await pvTable().signAndSave(pv);
|
|
190
|
+
expect(saved.versionTag).toBe("dev");
|
|
191
|
+
});
|
|
192
|
+
it("can signAndSave a beta version", async () => {
|
|
193
|
+
const pv = makePV({ versionTag: "beta" });
|
|
194
|
+
const saved = await pvTable().signAndSave(pv);
|
|
195
|
+
expect(saved.versionTag).toBe("beta");
|
|
196
|
+
});
|
|
197
|
+
it("can signAndSave a stable version", async () => {
|
|
198
|
+
const pv = makePV({ versionTag: "stable" });
|
|
199
|
+
const saved = await pvTable().signAndSave(pv);
|
|
200
|
+
expect(saved.versionTag).toBe("stable");
|
|
201
|
+
});
|
|
202
|
+
it("can promote an existing dev version to beta", async () => {
|
|
203
|
+
const pv = makePV({ versionTag: "dev" });
|
|
204
|
+
const saved = await pvTable().signAndSave(pv);
|
|
205
|
+
const promoted = { ...saved, versionTag: "beta" };
|
|
206
|
+
const result = await pvTable().signAndSave(promoted);
|
|
207
|
+
expect(result.versionTag).toBe("beta");
|
|
208
|
+
});
|
|
209
|
+
it("can promote an existing beta version to stable", async () => {
|
|
210
|
+
const pv = makePV({ versionTag: "beta" });
|
|
211
|
+
const saved = await pvTable().signAndSave(pv);
|
|
212
|
+
const promoted = { ...saved, versionTag: "stable" };
|
|
213
|
+
const result = await pvTable().signAndSave(promoted);
|
|
214
|
+
expect(result.versionTag).toBe("stable");
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
describe("personal space (no group)", () => {
|
|
218
|
+
it("allows Writer to signAndSave any tag without restriction", async () => {
|
|
219
|
+
activeSecretKey = writerKeys.secretKey;
|
|
220
|
+
const dc = userContext.userDataContext;
|
|
221
|
+
const table = (0, package_versions_1.PackageVersions)(dc);
|
|
222
|
+
const pv = makePV({ versionTag: "stable" });
|
|
223
|
+
const saved = await table.signAndSave(pv);
|
|
224
|
+
expect(saved.versionTag).toBe("stable");
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
});
|
package/dist/data/packages.js
CHANGED
|
@@ -106,14 +106,7 @@ let PackagesTable = (() => {
|
|
|
106
106
|
// users can do whatever they want to packages in their personal space
|
|
107
107
|
return super.save(packageObj, opts);
|
|
108
108
|
}
|
|
109
|
-
|
|
110
|
-
await (0, package_permissions_1.verifyPackageSignature)(packageObj, this.groupId);
|
|
111
|
-
}
|
|
112
|
-
catch (err) {
|
|
113
|
-
throw new Error("Package verification failed. Did you mean to call `signAndSave`?", {
|
|
114
|
-
cause: err,
|
|
115
|
-
});
|
|
116
|
-
}
|
|
109
|
+
await (0, package_permissions_1.verifyPackageSignature)(packageObj, this.groupId);
|
|
117
110
|
return super.save(packageObj, opts);
|
|
118
111
|
}
|
|
119
112
|
async signAndSave(packageObj, opts) {
|
|
@@ -121,7 +114,7 @@ let PackagesTable = (() => {
|
|
|
121
114
|
throw new Error("Package signing is not enabled. Call PackagesTable.enablePackageSigning(fn) to enable it.");
|
|
122
115
|
}
|
|
123
116
|
packageObj = await PackagesTable.addSignatureToPackage(packageObj);
|
|
124
|
-
return
|
|
117
|
+
return this.save(packageObj, opts);
|
|
125
118
|
}
|
|
126
119
|
static addSignatureToPackage = undefined;
|
|
127
120
|
static enablePackageSigning(fn) {
|