@lobb-js/lobb-ext-auth 0.11.3 → 0.13.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/dist/lib/components/pages/loginPage/index.svelte +1 -1
- package/dist/lib/components/pages/settings/pages/activityFeed.svelte +1 -1
- package/dist/lib/components/pages/settings/pages/rolesAndPermissions.svelte +1 -1
- package/dist/lib/components/pages/settings/pages/users.svelte +1 -1
- package/dist/lib/components/pages/userSettings/index.svelte +1 -1
- package/extensions/auth/studio/lib/components/pages/loginPage/index.svelte +1 -1
- package/extensions/auth/studio/lib/components/pages/settings/pages/activityFeed.svelte +1 -1
- package/extensions/auth/studio/lib/components/pages/settings/pages/rolesAndPermissions.svelte +1 -1
- package/extensions/auth/studio/lib/components/pages/settings/pages/users.svelte +1 -1
- package/extensions/auth/studio/lib/components/pages/userSettings/index.svelte +1 -1
- package/package.json +5 -5
- package/extensions/auth/tests/collections/extend_users_collection.test.ts +0 -61
- package/extensions/auth/tests/collections/shares.test.ts +0 -657
- package/extensions/auth/tests/configs/auth.ts +0 -101
- package/extensions/auth/tests/configs/auth_no_roles.ts +0 -65
- package/extensions/auth/tests/configs/auth_public_full_access.ts +0 -69
- package/extensions/auth/tests/configs/auth_with_admin_extra_fields.ts +0 -53
- package/extensions/auth/tests/configs/auth_with_different_admin_creds.ts +0 -81
- package/extensions/auth/tests/configs/auth_with_extend_users.ts +0 -79
- package/extensions/auth/tests/configs/auth_with_refresh_token.ts +0 -86
- package/extensions/auth/tests/configs/auth_with_short_access_token_only.ts +0 -95
- package/extensions/auth/tests/configs/auth_with_short_time_refresh_token.ts +0 -86
- package/extensions/auth/tests/configs/social_blog.ts +0 -146
- package/extensions/auth/tests/controllers/change_password.test.ts +0 -113
- package/extensions/auth/tests/controllers/dashboardAccessRoles.test.ts +0 -29
- package/extensions/auth/tests/controllers/login.test.ts +0 -101
- package/extensions/auth/tests/controllers/logout.test.ts +0 -89
- package/extensions/auth/tests/controllers/me.test.ts +0 -376
- package/extensions/auth/tests/controllers/register.test.ts +0 -45
- package/extensions/auth/tests/database/adminExtraFields.test.ts +0 -50
- package/extensions/auth/tests/database/db.test.ts +0 -64
- package/extensions/auth/tests/database/differentAdminCreds.test.ts +0 -51
- package/extensions/auth/tests/middlewares/adminAuthGuard.test.ts +0 -157
- package/extensions/auth/tests/middlewares/adminProtection.test.ts +0 -59
- package/extensions/auth/tests/middlewares/publicAllowBasic.test.ts +0 -137
- package/extensions/auth/tests/middlewares/publicPreventBasic.test.ts +0 -108
- package/extensions/auth/tests/permissions.test.ts +0 -127
- package/extensions/auth/tests/socialBlog.test.ts +0 -253
- package/extensions/auth/tests/utils/addArticles.ts +0 -22
- package/extensions/auth/tests/utils/addSocialBlogArticles.ts +0 -52
- package/extensions/auth/tests/utils/data/articles.ts +0 -65
- package/extensions/auth/tests/utils/data/socialBlogArticles.ts +0 -56
- package/extensions/auth/tests/workflows/shareIntersection.test.ts +0 -158
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { Lobb } from "@lobb-js/core";
|
|
2
|
-
import { afterAll, beforeAll, describe, it, expect } from "bun:test";
|
|
3
|
-
import { createArticles, removeArticles } from "../utils/addArticles.ts";
|
|
4
|
-
import { authNoRolesConfig } from "../configs/auth_no_roles.ts";
|
|
5
|
-
|
|
6
|
-
describe("Auth Guard for Admins", () => {
|
|
7
|
-
let lobb: Lobb;
|
|
8
|
-
let baseUrl: string;
|
|
9
|
-
let articlesIds: string[];
|
|
10
|
-
const authHeader = new Headers();
|
|
11
|
-
|
|
12
|
-
beforeAll(async () => {
|
|
13
|
-
lobb = await Lobb.init(authNoRolesConfig);
|
|
14
|
-
baseUrl = `http://127.0.0.1:${lobb.webServer.port}`;
|
|
15
|
-
articlesIds = await createArticles(lobb);
|
|
16
|
-
|
|
17
|
-
const response = await fetch(
|
|
18
|
-
`${baseUrl}/api/collections/auth_sessions`,
|
|
19
|
-
{
|
|
20
|
-
method: "POST",
|
|
21
|
-
headers: {
|
|
22
|
-
"Content-Type": "application/json",
|
|
23
|
-
},
|
|
24
|
-
body: JSON.stringify({
|
|
25
|
-
data: {
|
|
26
|
-
email: "admin@test.com",
|
|
27
|
-
password: "admin",
|
|
28
|
-
},
|
|
29
|
-
}),
|
|
30
|
-
},
|
|
31
|
-
);
|
|
32
|
-
const result = await response.json();
|
|
33
|
-
|
|
34
|
-
authHeader.append(
|
|
35
|
-
"Authorization",
|
|
36
|
-
`Bearer ${result.data.access_token.token}`,
|
|
37
|
-
);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
afterAll(async () => {
|
|
41
|
-
await removeArticles(lobb);
|
|
42
|
-
await lobb.close();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it("should allow admin users to create article", async () => {
|
|
46
|
-
const response = await fetch(
|
|
47
|
-
`${baseUrl}/api/collections/articles`,
|
|
48
|
-
{
|
|
49
|
-
method: "POST",
|
|
50
|
-
headers: authHeader,
|
|
51
|
-
body: JSON.stringify({
|
|
52
|
-
data: {
|
|
53
|
-
title: "Test Title",
|
|
54
|
-
body: "Test body",
|
|
55
|
-
published: true,
|
|
56
|
-
number_of_likes: 500,
|
|
57
|
-
},
|
|
58
|
-
}),
|
|
59
|
-
},
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
const result = await response.json();
|
|
63
|
-
|
|
64
|
-
expect(
|
|
65
|
-
result.data,
|
|
66
|
-
).toMatchObject({
|
|
67
|
-
body: "Test body",
|
|
68
|
-
title: "Test Title",
|
|
69
|
-
number_of_likes: 500,
|
|
70
|
-
published: true,
|
|
71
|
-
user_id: null,
|
|
72
|
-
});
|
|
73
|
-
expect(response.status).toEqual(201);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("should allow admin users to read articles", async () => {
|
|
77
|
-
const response = await fetch(
|
|
78
|
-
`${baseUrl}/api/collections/articles?sort=id`,
|
|
79
|
-
{
|
|
80
|
-
method: "GET",
|
|
81
|
-
headers: authHeader,
|
|
82
|
-
},
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
const result = await response.json();
|
|
86
|
-
const titles = result.data.map((article: any) => article.title);
|
|
87
|
-
|
|
88
|
-
expect(
|
|
89
|
-
titles,
|
|
90
|
-
).toEqual([
|
|
91
|
-
"The Future of AI: Trends Shaping Tomorrow's Technology",
|
|
92
|
-
"Climate Change and Its Impact on Global Food Security",
|
|
93
|
-
"The Rise of Remote Work: Is the Office Dead?",
|
|
94
|
-
"Blockchain Beyond Cryptocurrency: Practical Applications in 2024",
|
|
95
|
-
"Mental Health in the Digital Age: Navigating the New Challenges",
|
|
96
|
-
"Understanding Quantum Computing: The Next Tech Revolution",
|
|
97
|
-
"Sustainable Fashion: How the Industry is Going Green",
|
|
98
|
-
"The Role of 5G in Revolutionizing Smart Cities",
|
|
99
|
-
"Digital Marketing in 2024: Emerging Strategies for Success",
|
|
100
|
-
"The Future of Renewable Energy: Innovations Driving a Clean Energy Revolution",
|
|
101
|
-
"Test Title",
|
|
102
|
-
]);
|
|
103
|
-
expect(response.status).toEqual(200);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("should allow admin users to update an article", async () => {
|
|
107
|
-
const response = await fetch(
|
|
108
|
-
`${baseUrl}/api/collections/articles/${articlesIds[0]}`,
|
|
109
|
-
{
|
|
110
|
-
method: "PATCH",
|
|
111
|
-
headers: authHeader,
|
|
112
|
-
body: JSON.stringify({
|
|
113
|
-
data: {
|
|
114
|
-
title: "Edited Title",
|
|
115
|
-
body: "Edited body",
|
|
116
|
-
published: true,
|
|
117
|
-
number_of_likes: 500,
|
|
118
|
-
},
|
|
119
|
-
}),
|
|
120
|
-
},
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
const result = await response.json();
|
|
124
|
-
|
|
125
|
-
expect(
|
|
126
|
-
result.data,
|
|
127
|
-
).toMatchObject({
|
|
128
|
-
title: "Edited Title",
|
|
129
|
-
number_of_likes: 500,
|
|
130
|
-
published: true,
|
|
131
|
-
user_id: null,
|
|
132
|
-
});
|
|
133
|
-
expect(response.status).toEqual(200);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it("should allow admin users to delete an article", async () => {
|
|
137
|
-
const response = await fetch(
|
|
138
|
-
`${baseUrl}/api/collections/articles/${articlesIds[0]}`,
|
|
139
|
-
{
|
|
140
|
-
method: "DELETE",
|
|
141
|
-
headers: authHeader,
|
|
142
|
-
},
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
const result = await response.json();
|
|
146
|
-
|
|
147
|
-
expect(
|
|
148
|
-
result.data,
|
|
149
|
-
).toMatchObject({
|
|
150
|
-
title: "Edited Title",
|
|
151
|
-
number_of_likes: 500,
|
|
152
|
-
published: true,
|
|
153
|
-
user_id: null,
|
|
154
|
-
});
|
|
155
|
-
expect(response.status).toEqual(200);
|
|
156
|
-
});
|
|
157
|
-
});
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { Lobb } from "@lobb-js/core";
|
|
2
|
-
import { afterAll, beforeAll, describe, it, expect } from "bun:test";
|
|
3
|
-
import { authNoRolesConfig } from "../configs/auth_no_roles.ts";
|
|
4
|
-
|
|
5
|
-
describe("Admin user protection", () => {
|
|
6
|
-
let lobb: Lobb;
|
|
7
|
-
let baseUrl: string;
|
|
8
|
-
const authHeader = new Headers();
|
|
9
|
-
|
|
10
|
-
beforeAll(async () => {
|
|
11
|
-
lobb = await Lobb.init(authNoRolesConfig);
|
|
12
|
-
baseUrl = `http://127.0.0.1:${lobb.webServer.port}`;
|
|
13
|
-
|
|
14
|
-
const response = await fetch(
|
|
15
|
-
`${baseUrl}/api/collections/auth_sessions`,
|
|
16
|
-
{
|
|
17
|
-
method: "POST",
|
|
18
|
-
headers: { "Content-Type": "application/json" },
|
|
19
|
-
body: JSON.stringify({
|
|
20
|
-
data: { email: "admin@test.com", password: "admin" },
|
|
21
|
-
}),
|
|
22
|
-
},
|
|
23
|
-
);
|
|
24
|
-
const result = await response.json();
|
|
25
|
-
authHeader.append("Authorization", `Bearer ${result.data.access_token.token}`);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
afterAll(async () => {
|
|
29
|
-
await lobb.close();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("should prevent deleting the admin user", async () => {
|
|
33
|
-
const response = await fetch(
|
|
34
|
-
`${baseUrl}/api/collections/auth_users/1`,
|
|
35
|
-
{ method: "DELETE", headers: authHeader },
|
|
36
|
-
);
|
|
37
|
-
const result = await response.json();
|
|
38
|
-
|
|
39
|
-
expect(response.status).toEqual(403);
|
|
40
|
-
expect(result.message).toEqual("The admin user cannot be deleted.");
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("should prevent updating the admin user", async () => {
|
|
44
|
-
const response = await fetch(
|
|
45
|
-
`${baseUrl}/api/collections/auth_users/1`,
|
|
46
|
-
{
|
|
47
|
-
method: "PATCH",
|
|
48
|
-
headers: authHeader,
|
|
49
|
-
body: JSON.stringify({
|
|
50
|
-
data: { email: "hacked@example.com" },
|
|
51
|
-
}),
|
|
52
|
-
},
|
|
53
|
-
);
|
|
54
|
-
const result = await response.json();
|
|
55
|
-
|
|
56
|
-
expect(response.status).toEqual(403);
|
|
57
|
-
expect(result.message).toEqual("The admin user cannot be updated.");
|
|
58
|
-
});
|
|
59
|
-
});
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { Lobb } from "@lobb-js/core";
|
|
2
|
-
import { afterAll, beforeAll, describe, it, expect } from "bun:test";
|
|
3
|
-
import { createArticles, removeArticles } from "../utils/addArticles.ts";
|
|
4
|
-
import { authPublicFullAccessConfig } from "../configs/auth_public_full_access.ts";
|
|
5
|
-
|
|
6
|
-
describe("Allow Guard For Public Users", () => {
|
|
7
|
-
let lobb: Lobb;
|
|
8
|
-
let baseUrl: string;
|
|
9
|
-
let articlesIds: string[];
|
|
10
|
-
|
|
11
|
-
beforeAll(async () => {
|
|
12
|
-
lobb = await Lobb.init(authPublicFullAccessConfig);
|
|
13
|
-
baseUrl = `http://127.0.0.1:${lobb.webServer.port}`;
|
|
14
|
-
articlesIds = await createArticles(lobb);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
afterAll(async () => {
|
|
18
|
-
await removeArticles(lobb);
|
|
19
|
-
await lobb.close();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("should allow public users to create an articles", async () => {
|
|
23
|
-
const response = await fetch(
|
|
24
|
-
`${baseUrl}/api/collections/articles`,
|
|
25
|
-
{
|
|
26
|
-
method: "POST",
|
|
27
|
-
body: JSON.stringify({
|
|
28
|
-
data: {
|
|
29
|
-
title: "Test Title",
|
|
30
|
-
body: "Test body",
|
|
31
|
-
published: true,
|
|
32
|
-
number_of_likes: 500,
|
|
33
|
-
},
|
|
34
|
-
}),
|
|
35
|
-
},
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
const result = await response.json();
|
|
39
|
-
|
|
40
|
-
expect(
|
|
41
|
-
result.data,
|
|
42
|
-
).toMatchObject({
|
|
43
|
-
title: "Test Title",
|
|
44
|
-
body: "Test body",
|
|
45
|
-
published: true,
|
|
46
|
-
number_of_likes: 500,
|
|
47
|
-
user_id: null,
|
|
48
|
-
});
|
|
49
|
-
expect(response.status).toEqual(201);
|
|
50
|
-
|
|
51
|
-
await lobb.collectionService.deleteMany({
|
|
52
|
-
collectionName: "articles",
|
|
53
|
-
filter: {
|
|
54
|
-
title: "Test Title",
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("should allow public users to read an articles", async () => {
|
|
60
|
-
const response = await fetch(
|
|
61
|
-
`${baseUrl}/api/collections/articles?sort=id`,
|
|
62
|
-
{
|
|
63
|
-
method: "GET",
|
|
64
|
-
},
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
const result = await response.json();
|
|
68
|
-
const titles = result.data.map((article: any) => article.title);
|
|
69
|
-
|
|
70
|
-
expect(
|
|
71
|
-
titles,
|
|
72
|
-
).toEqual([
|
|
73
|
-
"The Future of AI: Trends Shaping Tomorrow's Technology",
|
|
74
|
-
"Climate Change and Its Impact on Global Food Security",
|
|
75
|
-
"The Rise of Remote Work: Is the Office Dead?",
|
|
76
|
-
"Blockchain Beyond Cryptocurrency: Practical Applications in 2024",
|
|
77
|
-
"Mental Health in the Digital Age: Navigating the New Challenges",
|
|
78
|
-
"Understanding Quantum Computing: The Next Tech Revolution",
|
|
79
|
-
"Sustainable Fashion: How the Industry is Going Green",
|
|
80
|
-
"The Role of 5G in Revolutionizing Smart Cities",
|
|
81
|
-
"Digital Marketing in 2024: Emerging Strategies for Success",
|
|
82
|
-
"The Future of Renewable Energy: Innovations Driving a Clean Energy Revolution",
|
|
83
|
-
]);
|
|
84
|
-
expect(response.status).toEqual(200);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it("should allow public users to update an articles", async () => {
|
|
88
|
-
const response = await fetch(
|
|
89
|
-
`${baseUrl}/api/collections/articles/${articlesIds[0]}`,
|
|
90
|
-
{
|
|
91
|
-
method: "PATCH",
|
|
92
|
-
body: JSON.stringify({
|
|
93
|
-
data: {
|
|
94
|
-
title: "Edited Title",
|
|
95
|
-
body: "Edited body",
|
|
96
|
-
published: false,
|
|
97
|
-
number_of_likes: 400,
|
|
98
|
-
},
|
|
99
|
-
}),
|
|
100
|
-
},
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
const result = await response.json();
|
|
104
|
-
|
|
105
|
-
expect(
|
|
106
|
-
result.data,
|
|
107
|
-
).toMatchObject({
|
|
108
|
-
number_of_likes: 400,
|
|
109
|
-
published: false,
|
|
110
|
-
title: "Edited Title",
|
|
111
|
-
user_id: null,
|
|
112
|
-
});
|
|
113
|
-
expect(response.status).toEqual(200);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("should allow public users to delete an articles", async () => {
|
|
117
|
-
const response = await fetch(
|
|
118
|
-
`${baseUrl}/api/collections/articles/${articlesIds[0]}`,
|
|
119
|
-
{
|
|
120
|
-
method: "DELETE",
|
|
121
|
-
},
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
const result = await response.json();
|
|
125
|
-
|
|
126
|
-
expect(
|
|
127
|
-
result.data,
|
|
128
|
-
).toMatchObject({
|
|
129
|
-
body: "Edited body",
|
|
130
|
-
number_of_likes: 400,
|
|
131
|
-
published: false,
|
|
132
|
-
title: "Edited Title",
|
|
133
|
-
user_id: null,
|
|
134
|
-
});
|
|
135
|
-
expect(response.status).toEqual(200);
|
|
136
|
-
});
|
|
137
|
-
});
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { Lobb } from "@lobb-js/core";
|
|
2
|
-
import { afterAll, beforeAll, describe, it, expect } from "bun:test";
|
|
3
|
-
import { createArticles, removeArticles } from "../utils/addArticles.ts";
|
|
4
|
-
import { authNoRolesConfig } from "../configs/auth_no_roles.ts";
|
|
5
|
-
|
|
6
|
-
describe("Prevent Guard For Public Users", () => {
|
|
7
|
-
let lobb: Lobb;
|
|
8
|
-
let baseUrl: string;
|
|
9
|
-
let articlesIds: string[];
|
|
10
|
-
|
|
11
|
-
beforeAll(async () => {
|
|
12
|
-
lobb = await Lobb.init(authNoRolesConfig);
|
|
13
|
-
baseUrl = `http://127.0.0.1:${lobb.webServer.port}`;
|
|
14
|
-
articlesIds = await createArticles(lobb);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
afterAll(async () => {
|
|
18
|
-
await removeArticles(lobb);
|
|
19
|
-
await lobb.close();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("should prevent public users from creating article", async () => {
|
|
23
|
-
const response = await fetch(
|
|
24
|
-
`${baseUrl}/api/collections/articles`,
|
|
25
|
-
{
|
|
26
|
-
method: "POST",
|
|
27
|
-
body: JSON.stringify({
|
|
28
|
-
data: {
|
|
29
|
-
title: "Edited Title",
|
|
30
|
-
body: "Edited body",
|
|
31
|
-
number_of_likes: 500,
|
|
32
|
-
published: true,
|
|
33
|
-
},
|
|
34
|
-
}),
|
|
35
|
-
},
|
|
36
|
-
);
|
|
37
|
-
const result = await response.json();
|
|
38
|
-
|
|
39
|
-
expect(
|
|
40
|
-
result.message,
|
|
41
|
-
).toEqual("You do not have permission to perform this action.");
|
|
42
|
-
expect(response.status).toEqual(403);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it("should prevent public users from reading users", async () => {
|
|
46
|
-
const response = await fetch(
|
|
47
|
-
`${baseUrl}/api/collections/auth_users`,
|
|
48
|
-
{
|
|
49
|
-
method: "GET",
|
|
50
|
-
},
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
const result = await response.json();
|
|
54
|
-
|
|
55
|
-
expect(
|
|
56
|
-
result.message,
|
|
57
|
-
).toEqual("You do not have permission to perform this action.");
|
|
58
|
-
expect(response.status).toEqual(403);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("should prevent public users from updating an article", async () => {
|
|
62
|
-
const response = await fetch(
|
|
63
|
-
`${baseUrl}/api/collections/articles/${articlesIds[0]}`,
|
|
64
|
-
{
|
|
65
|
-
method: "PATCH",
|
|
66
|
-
body: JSON.stringify({
|
|
67
|
-
data: {
|
|
68
|
-
title: "Edited Title",
|
|
69
|
-
body: "Edited body",
|
|
70
|
-
published: true,
|
|
71
|
-
number_of_likes: 500,
|
|
72
|
-
},
|
|
73
|
-
}),
|
|
74
|
-
},
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
const result = await response.json();
|
|
78
|
-
|
|
79
|
-
expect(
|
|
80
|
-
result,
|
|
81
|
-
).toEqual({
|
|
82
|
-
status: 403,
|
|
83
|
-
code: "FORBIDDEN",
|
|
84
|
-
message: "You do not have permission to perform this action.",
|
|
85
|
-
});
|
|
86
|
-
expect(response.status).toEqual(403);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it("should prevent public users from deleting an article", async () => {
|
|
90
|
-
const response = await fetch(
|
|
91
|
-
`${baseUrl}/api/collections/articles/${articlesIds[0]}`,
|
|
92
|
-
{
|
|
93
|
-
method: "DELETE",
|
|
94
|
-
},
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
const result = await response.json();
|
|
98
|
-
|
|
99
|
-
expect(
|
|
100
|
-
result,
|
|
101
|
-
).toEqual({
|
|
102
|
-
code: "FORBIDDEN",
|
|
103
|
-
message: "You do not have permission to perform this action.",
|
|
104
|
-
status: 403,
|
|
105
|
-
});
|
|
106
|
-
expect(response.status).toEqual(403);
|
|
107
|
-
});
|
|
108
|
-
});
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test";
|
|
2
|
-
import { isActionAllowed } from "../studio/shared/permissions.ts";
|
|
3
|
-
|
|
4
|
-
// Default-deny is the contract: only explicit `true` or an object that
|
|
5
|
-
// actually lists at least one constraint counts as a grant. Anything empty
|
|
6
|
-
// (`undefined`, `{}`, `null`) at ANY level — role / collection / action —
|
|
7
|
-
// must collapse to "no grant". These tests pin that invariant so a future
|
|
8
|
-
// refactor can't accidentally turn `{}` back into "allow".
|
|
9
|
-
describe("isActionAllowed — empty {} means false at every level", () => {
|
|
10
|
-
describe("role permissions level", () => {
|
|
11
|
-
it("returns false for undefined permissions", () => {
|
|
12
|
-
expect(isActionAllowed("articles", "read", undefined)).toBe(false);
|
|
13
|
-
expect(isActionAllowed("articles", "create", undefined)).toBe(false);
|
|
14
|
-
expect(isActionAllowed("articles", "update", undefined)).toBe(false);
|
|
15
|
-
expect(isActionAllowed("articles", "delete", undefined)).toBe(false);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("returns false for an empty {} permissions object", () => {
|
|
19
|
-
expect(isActionAllowed("articles", "read", {})).toBe(false);
|
|
20
|
-
expect(isActionAllowed("articles", "create", {})).toBe(false);
|
|
21
|
-
expect(isActionAllowed("articles", "update", {})).toBe(false);
|
|
22
|
-
expect(isActionAllowed("articles", "delete", {})).toBe(false);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("returns true only for the explicit `true` shortcut (admin)", () => {
|
|
26
|
-
expect(isActionAllowed("articles", "read", true)).toBe(true);
|
|
27
|
-
expect(isActionAllowed("any_collection_name", "delete", true)).toBe(true);
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe("collection level", () => {
|
|
32
|
-
it("returns false when the collection is missing from permissions", () => {
|
|
33
|
-
const perms = { articles: { read: true } };
|
|
34
|
-
expect(isActionAllowed("auth_users", "read", perms)).toBe(false);
|
|
35
|
-
expect(isActionAllowed("auth_users", "create", perms)).toBe(false);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("returns false when the collection's value is explicitly undefined", () => {
|
|
39
|
-
const perms = { articles: undefined };
|
|
40
|
-
expect(isActionAllowed("articles", "read", perms)).toBe(false);
|
|
41
|
-
expect(isActionAllowed("articles", "create", perms)).toBe(false);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("returns false when the collection is an empty {}", () => {
|
|
45
|
-
const perms = { articles: {} };
|
|
46
|
-
expect(isActionAllowed("articles", "read", perms)).toBe(false);
|
|
47
|
-
expect(isActionAllowed("articles", "create", perms)).toBe(false);
|
|
48
|
-
expect(isActionAllowed("articles", "update", perms)).toBe(false);
|
|
49
|
-
expect(isActionAllowed("articles", "delete", perms)).toBe(false);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("returns true when the collection is explicitly `true`", () => {
|
|
53
|
-
const perms = { articles: true as const };
|
|
54
|
-
expect(isActionAllowed("articles", "read", perms)).toBe(true);
|
|
55
|
-
expect(isActionAllowed("articles", "create", perms)).toBe(true);
|
|
56
|
-
expect(isActionAllowed("articles", "update", perms)).toBe(true);
|
|
57
|
-
expect(isActionAllowed("articles", "delete", perms)).toBe(true);
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe("action level", () => {
|
|
62
|
-
it("returns false when the action is missing from the collection", () => {
|
|
63
|
-
const perms = { articles: { read: true } };
|
|
64
|
-
expect(isActionAllowed("articles", "create", perms)).toBe(false);
|
|
65
|
-
expect(isActionAllowed("articles", "update", perms)).toBe(false);
|
|
66
|
-
expect(isActionAllowed("articles", "delete", perms)).toBe(false);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("returns false when the action is an empty {} — no constraints listed", () => {
|
|
70
|
-
// This is the footgun the rule fixes: an empty constraint object used
|
|
71
|
-
// to count as "conditional grant with no constraints" → effectively
|
|
72
|
-
// unconditional allow. Now it collapses to no grant.
|
|
73
|
-
expect(isActionAllowed("articles", "read", { articles: { read: {} } })).toBe(false);
|
|
74
|
-
expect(isActionAllowed("articles", "create", { articles: { create: {} } })).toBe(false);
|
|
75
|
-
expect(isActionAllowed("articles", "update", { articles: { update: {} } })).toBe(false);
|
|
76
|
-
expect(isActionAllowed("articles", "delete", { articles: { delete: {} } })).toBe(false);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it("returns false when the action is explicitly undefined", () => {
|
|
80
|
-
const perms = { articles: { read: undefined, create: undefined } };
|
|
81
|
-
expect(isActionAllowed("articles", "read", perms)).toBe(false);
|
|
82
|
-
expect(isActionAllowed("articles", "create", perms)).toBe(false);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("returns true when the action is explicitly `true`", () => {
|
|
86
|
-
expect(isActionAllowed("articles", "read", { articles: { read: true } })).toBe(true);
|
|
87
|
-
expect(isActionAllowed("articles", "create", { articles: { create: true } })).toBe(true);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("returns true when the action has at least one real constraint", () => {
|
|
91
|
-
// `filter` for read
|
|
92
|
-
expect(
|
|
93
|
-
isActionAllowed("articles", "read", {
|
|
94
|
-
articles: { read: { filter: { id: 1 } } },
|
|
95
|
-
}),
|
|
96
|
-
).toBe(true);
|
|
97
|
-
// `fields` allowlist for read
|
|
98
|
-
expect(
|
|
99
|
-
isActionAllowed("articles", "read", {
|
|
100
|
-
articles: { read: { fields: { title: true } } },
|
|
101
|
-
}),
|
|
102
|
-
).toBe(true);
|
|
103
|
-
// `fields` allowlist for create
|
|
104
|
-
expect(
|
|
105
|
-
isActionAllowed("articles", "create", {
|
|
106
|
-
articles: { create: { fields: { title: true } } },
|
|
107
|
-
}),
|
|
108
|
-
).toBe(true);
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
describe("cross-level: only the relevant level needs to grant", () => {
|
|
113
|
-
it("an empty {} at any single level kills the chain regardless of siblings", () => {
|
|
114
|
-
// Other collections have grants, but `articles` is `{}` → no articles access.
|
|
115
|
-
const perms = { auth_users: { read: true }, articles: {} };
|
|
116
|
-
expect(isActionAllowed("articles", "read", perms)).toBe(false);
|
|
117
|
-
// Sibling still works.
|
|
118
|
-
expect(isActionAllowed("auth_users", "read", perms)).toBe(true);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it("an empty {} action doesn't bleed into other actions on the same collection", () => {
|
|
122
|
-
const perms = { articles: { read: true, create: {} } };
|
|
123
|
-
expect(isActionAllowed("articles", "read", perms)).toBe(true);
|
|
124
|
-
expect(isActionAllowed("articles", "create", perms)).toBe(false);
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
});
|