@webstudio-is/trpc-interface 0.56.0 → 0.58.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.
|
@@ -15,70 +15,82 @@ const registerProjectOwner = async (props, context) => {
|
|
|
15
15
|
});
|
|
16
16
|
};
|
|
17
17
|
const hasProjectPermit = async (props, context) => {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
if (authorization.userId !== void 0) {
|
|
29
|
-
checks.push(
|
|
30
|
-
authorizeTrpc.check.query({
|
|
31
|
-
subjectSet: {
|
|
32
|
-
namespace: "User",
|
|
33
|
-
id: authorization.userId
|
|
34
|
-
},
|
|
35
|
-
namespace,
|
|
36
|
-
id: props.projectId,
|
|
37
|
-
permit: props.permit
|
|
38
|
-
})
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
if (authorization.authToken !== void 0 && props.permit !== "own") {
|
|
42
|
-
checks.push(
|
|
43
|
-
authorizeTrpc.check.query({
|
|
44
|
-
namespace,
|
|
45
|
-
id: props.projectId,
|
|
46
|
-
subjectSet: {
|
|
47
|
-
id: authorization.authToken,
|
|
48
|
-
namespace: "Token"
|
|
49
|
-
},
|
|
50
|
-
permit: props.permit
|
|
51
|
-
})
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
if (checks.length === 0) {
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
const authResults = await Promise.allSettled(checks);
|
|
58
|
-
for (const authResult of authResults) {
|
|
59
|
-
if (authResult.status === "rejected") {
|
|
60
|
-
throw new Error(`Authorization call failed ${authResult.reason}`);
|
|
18
|
+
const start = Date.now();
|
|
19
|
+
try {
|
|
20
|
+
const { authorization } = context;
|
|
21
|
+
const { authorizeTrpc } = authorization;
|
|
22
|
+
const checks = [];
|
|
23
|
+
const namespace = "Project";
|
|
24
|
+
if (props.permit === "view" && context.authorization.buildEnv === "prod") {
|
|
25
|
+
return true;
|
|
61
26
|
}
|
|
27
|
+
if (props.permit === "view" && props.projectId === "62154aaef0cb0860ccf85d6e") {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
if (authorization.userId !== void 0) {
|
|
31
|
+
checks.push(
|
|
32
|
+
authorizeTrpc.check.query({
|
|
33
|
+
subjectSet: {
|
|
34
|
+
namespace: "User",
|
|
35
|
+
id: authorization.userId
|
|
36
|
+
},
|
|
37
|
+
namespace,
|
|
38
|
+
id: props.projectId,
|
|
39
|
+
permit: props.permit
|
|
40
|
+
})
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
if (authorization.authToken !== void 0 && props.permit !== "own") {
|
|
44
|
+
checks.push(
|
|
45
|
+
authorizeTrpc.check.query({
|
|
46
|
+
namespace,
|
|
47
|
+
id: props.projectId,
|
|
48
|
+
subjectSet: {
|
|
49
|
+
id: authorization.authToken,
|
|
50
|
+
namespace: "Token"
|
|
51
|
+
},
|
|
52
|
+
permit: props.permit
|
|
53
|
+
})
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (checks.length === 0) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
const authResults = await Promise.allSettled(checks);
|
|
60
|
+
for (const authResult of authResults) {
|
|
61
|
+
if (authResult.status === "rejected") {
|
|
62
|
+
throw new Error(`Authorization call failed ${authResult.reason}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const allowed = authResults.some(
|
|
66
|
+
(authResult) => authResult.status === "fulfilled" && authResult.value.allowed
|
|
67
|
+
);
|
|
68
|
+
return allowed;
|
|
69
|
+
} finally {
|
|
70
|
+
const diff = Date.now() - start;
|
|
71
|
+
console.log(`hasProjectPermit execution ${diff}ms`);
|
|
62
72
|
}
|
|
63
|
-
const allowed = authResults.some(
|
|
64
|
-
(authResult) => authResult.status === "fulfilled" && authResult.value.allowed
|
|
65
|
-
);
|
|
66
|
-
return allowed;
|
|
67
73
|
};
|
|
68
74
|
const getProjectPermit = async (props, context) => {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
permitToCheck.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
75
|
+
const start = Date.now();
|
|
76
|
+
try {
|
|
77
|
+
const permitToCheck = props.permits;
|
|
78
|
+
const permits = await Promise.allSettled(
|
|
79
|
+
permitToCheck.map(
|
|
80
|
+
(permit) => hasProjectPermit({ projectId: props.projectId, permit }, context)
|
|
81
|
+
)
|
|
82
|
+
);
|
|
83
|
+
for (const permit of permits) {
|
|
84
|
+
if (permit.status === "rejected") {
|
|
85
|
+
throw new Error(`Authorization call failed ${permit.reason}`);
|
|
86
|
+
}
|
|
87
|
+
if (permit.value === true) {
|
|
88
|
+
return permitToCheck[permits.indexOf(permit)];
|
|
89
|
+
}
|
|
81
90
|
}
|
|
91
|
+
} finally {
|
|
92
|
+
const diff = Date.now() - start;
|
|
93
|
+
console.log(`getProjectPermit execution ${diff}ms`);
|
|
82
94
|
}
|
|
83
95
|
};
|
|
84
96
|
export {
|
|
@@ -40,69 +40,81 @@ const registerProjectOwner = async (props, context) => {
|
|
|
40
40
|
});
|
|
41
41
|
};
|
|
42
42
|
const hasProjectPermit = async (props, context) => {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
if (authorization.userId !== void 0) {
|
|
54
|
-
checks.push(
|
|
55
|
-
authorizeTrpc.check.query({
|
|
56
|
-
subjectSet: {
|
|
57
|
-
namespace: "User",
|
|
58
|
-
id: authorization.userId
|
|
59
|
-
},
|
|
60
|
-
namespace,
|
|
61
|
-
id: props.projectId,
|
|
62
|
-
permit: props.permit
|
|
63
|
-
})
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
if (authorization.authToken !== void 0 && props.permit !== "own") {
|
|
67
|
-
checks.push(
|
|
68
|
-
authorizeTrpc.check.query({
|
|
69
|
-
namespace,
|
|
70
|
-
id: props.projectId,
|
|
71
|
-
subjectSet: {
|
|
72
|
-
id: authorization.authToken,
|
|
73
|
-
namespace: "Token"
|
|
74
|
-
},
|
|
75
|
-
permit: props.permit
|
|
76
|
-
})
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
if (checks.length === 0) {
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
const authResults = await Promise.allSettled(checks);
|
|
83
|
-
for (const authResult of authResults) {
|
|
84
|
-
if (authResult.status === "rejected") {
|
|
85
|
-
throw new Error(`Authorization call failed ${authResult.reason}`);
|
|
43
|
+
const start = Date.now();
|
|
44
|
+
try {
|
|
45
|
+
const { authorization } = context;
|
|
46
|
+
const { authorizeTrpc } = authorization;
|
|
47
|
+
const checks = [];
|
|
48
|
+
const namespace = "Project";
|
|
49
|
+
if (props.permit === "view" && context.authorization.buildEnv === "prod") {
|
|
50
|
+
return true;
|
|
86
51
|
}
|
|
52
|
+
if (props.permit === "view" && props.projectId === "62154aaef0cb0860ccf85d6e") {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
if (authorization.userId !== void 0) {
|
|
56
|
+
checks.push(
|
|
57
|
+
authorizeTrpc.check.query({
|
|
58
|
+
subjectSet: {
|
|
59
|
+
namespace: "User",
|
|
60
|
+
id: authorization.userId
|
|
61
|
+
},
|
|
62
|
+
namespace,
|
|
63
|
+
id: props.projectId,
|
|
64
|
+
permit: props.permit
|
|
65
|
+
})
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
if (authorization.authToken !== void 0 && props.permit !== "own") {
|
|
69
|
+
checks.push(
|
|
70
|
+
authorizeTrpc.check.query({
|
|
71
|
+
namespace,
|
|
72
|
+
id: props.projectId,
|
|
73
|
+
subjectSet: {
|
|
74
|
+
id: authorization.authToken,
|
|
75
|
+
namespace: "Token"
|
|
76
|
+
},
|
|
77
|
+
permit: props.permit
|
|
78
|
+
})
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
if (checks.length === 0) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
const authResults = await Promise.allSettled(checks);
|
|
85
|
+
for (const authResult of authResults) {
|
|
86
|
+
if (authResult.status === "rejected") {
|
|
87
|
+
throw new Error(`Authorization call failed ${authResult.reason}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const allowed = authResults.some(
|
|
91
|
+
(authResult) => authResult.status === "fulfilled" && authResult.value.allowed
|
|
92
|
+
);
|
|
93
|
+
return allowed;
|
|
94
|
+
} finally {
|
|
95
|
+
const diff = Date.now() - start;
|
|
96
|
+
console.log(`hasProjectPermit execution ${diff}ms`);
|
|
87
97
|
}
|
|
88
|
-
const allowed = authResults.some(
|
|
89
|
-
(authResult) => authResult.status === "fulfilled" && authResult.value.allowed
|
|
90
|
-
);
|
|
91
|
-
return allowed;
|
|
92
98
|
};
|
|
93
99
|
const getProjectPermit = async (props, context) => {
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
permitToCheck.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
100
|
+
const start = Date.now();
|
|
101
|
+
try {
|
|
102
|
+
const permitToCheck = props.permits;
|
|
103
|
+
const permits = await Promise.allSettled(
|
|
104
|
+
permitToCheck.map(
|
|
105
|
+
(permit) => hasProjectPermit({ projectId: props.projectId, permit }, context)
|
|
106
|
+
)
|
|
107
|
+
);
|
|
108
|
+
for (const permit of permits) {
|
|
109
|
+
if (permit.status === "rejected") {
|
|
110
|
+
throw new Error(`Authorization call failed ${permit.reason}`);
|
|
111
|
+
}
|
|
112
|
+
if (permit.value === true) {
|
|
113
|
+
return permitToCheck[permits.indexOf(permit)];
|
|
114
|
+
}
|
|
106
115
|
}
|
|
116
|
+
} finally {
|
|
117
|
+
const diff = Date.now() - start;
|
|
118
|
+
console.log(`getProjectPermit execution ${diff}ms`);
|
|
107
119
|
}
|
|
108
120
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webstudio-is/trpc-interface",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.58.0",
|
|
4
4
|
"description": "Webstudio TRPC Interface",
|
|
5
5
|
"author": "Webstudio <github@webstudio.is>",
|
|
6
6
|
"homepage": "https://webstudio.is",
|
|
@@ -10,14 +10,14 @@
|
|
|
10
10
|
"@trpc/server": "^10.9.0",
|
|
11
11
|
"uuid": "^9.0.0",
|
|
12
12
|
"zod": "^3.19.1",
|
|
13
|
-
"@webstudio-is/prisma-client": "^0.
|
|
13
|
+
"@webstudio-is/prisma-client": "^0.58.0"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@types/node": "^18.11.18",
|
|
17
17
|
"typescript": "5.0.3",
|
|
18
|
-
"@webstudio-is/jest-config": "^1.0.
|
|
18
|
+
"@webstudio-is/jest-config": "^1.0.5",
|
|
19
19
|
"@webstudio-is/scripts": "^0.0.0",
|
|
20
|
-
"@webstudio-is/tsconfig": "^1.0.
|
|
20
|
+
"@webstudio-is/tsconfig": "^1.0.5"
|
|
21
21
|
},
|
|
22
22
|
"exports": {
|
|
23
23
|
"./server": {
|
|
@@ -34,12 +34,12 @@
|
|
|
34
34
|
"private": false,
|
|
35
35
|
"sideEffects": false,
|
|
36
36
|
"scripts": {
|
|
37
|
-
"typecheck": "tsc --noEmit",
|
|
37
|
+
"typecheck": "tsc --noEmit --emitDeclarationOnly false",
|
|
38
38
|
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
39
39
|
"checks": "pnpm typecheck && pnpm lint",
|
|
40
40
|
"dev": "build-package --watch",
|
|
41
41
|
"build": "build-package",
|
|
42
|
-
"dts": "tsc --
|
|
42
|
+
"dts": "tsc --declarationDir lib/types",
|
|
43
43
|
"lint": "eslint ./src --ext .ts,.tsx --max-warnings 0"
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -37,77 +37,86 @@ export const hasProjectPermit = async (
|
|
|
37
37
|
},
|
|
38
38
|
context: AppContext
|
|
39
39
|
) => {
|
|
40
|
-
const
|
|
41
|
-
const { authorizeTrpc } = authorization;
|
|
40
|
+
const start = Date.now();
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
try {
|
|
43
|
+
const { authorization } = context;
|
|
44
|
+
const { authorizeTrpc } = authorization;
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
46
|
+
const checks = [];
|
|
47
|
+
const namespace = "Project";
|
|
50
48
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (
|
|
56
|
-
props.permit === "view" &&
|
|
57
|
-
props.projectId === "62154aaef0cb0860ccf85d6e"
|
|
58
|
-
) {
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
49
|
+
// Allow load production build env i.e. "published" site
|
|
50
|
+
if (props.permit === "view" && context.authorization.buildEnv === "prod") {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
61
53
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
permit: props.permit,
|
|
73
|
-
})
|
|
74
|
-
);
|
|
75
|
-
}
|
|
54
|
+
// Allow load webstudiois for clone
|
|
55
|
+
// @todo Rethink permissions for this use-case
|
|
56
|
+
// The plan is to make new permission for projects which are allowed to be publicly clonable by anyone
|
|
57
|
+
// https://github.com/webstudio-is/webstudio-builder/issues/1038
|
|
58
|
+
if (
|
|
59
|
+
props.permit === "view" &&
|
|
60
|
+
props.projectId === "62154aaef0cb0860ccf85d6e"
|
|
61
|
+
) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
76
64
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
65
|
+
// Check if the user is allowed to access the project
|
|
66
|
+
if (authorization.userId !== undefined) {
|
|
67
|
+
checks.push(
|
|
68
|
+
authorizeTrpc.check.query({
|
|
69
|
+
subjectSet: {
|
|
70
|
+
namespace: "User",
|
|
71
|
+
id: authorization.userId,
|
|
72
|
+
},
|
|
73
|
+
namespace,
|
|
74
|
+
id: props.projectId,
|
|
75
|
+
permit: props.permit,
|
|
76
|
+
})
|
|
77
|
+
);
|
|
78
|
+
}
|
|
92
79
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
80
|
+
// Check if the special link with a token allows to access the project
|
|
81
|
+
// Token doesn't have own permit, do not check it
|
|
82
|
+
if (authorization.authToken !== undefined && props.permit !== "own") {
|
|
83
|
+
checks.push(
|
|
84
|
+
authorizeTrpc.check.query({
|
|
85
|
+
namespace,
|
|
86
|
+
id: props.projectId,
|
|
87
|
+
subjectSet: {
|
|
88
|
+
id: authorization.authToken,
|
|
89
|
+
namespace: "Token",
|
|
90
|
+
},
|
|
91
|
+
permit: props.permit,
|
|
92
|
+
})
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (checks.length === 0) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
96
99
|
|
|
97
|
-
|
|
100
|
+
const authResults = await Promise.allSettled(checks);
|
|
98
101
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
+
for (const authResult of authResults) {
|
|
103
|
+
if (authResult.status === "rejected") {
|
|
104
|
+
throw new Error(`Authorization call failed ${authResult.reason}`);
|
|
105
|
+
}
|
|
102
106
|
}
|
|
103
|
-
}
|
|
104
107
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
const allowed = authResults.some(
|
|
109
|
+
(authResult) =>
|
|
110
|
+
authResult.status === "fulfilled" && authResult.value.allowed
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
return allowed;
|
|
114
|
+
} finally {
|
|
115
|
+
const diff = Date.now() - start;
|
|
109
116
|
|
|
110
|
-
|
|
117
|
+
// eslint-disable-next-line no-console
|
|
118
|
+
console.log(`hasProjectPermit execution ${diff}ms`);
|
|
119
|
+
}
|
|
111
120
|
};
|
|
112
121
|
|
|
113
122
|
/**
|
|
@@ -122,21 +131,30 @@ export const getProjectPermit = async <T extends AuthPermit>(
|
|
|
122
131
|
},
|
|
123
132
|
context: AppContext
|
|
124
133
|
): Promise<T | undefined> => {
|
|
125
|
-
const
|
|
134
|
+
const start = Date.now();
|
|
126
135
|
|
|
127
|
-
|
|
128
|
-
permitToCheck.
|
|
129
|
-
hasProjectPermit({ projectId: props.projectId, permit }, context)
|
|
130
|
-
)
|
|
131
|
-
);
|
|
136
|
+
try {
|
|
137
|
+
const permitToCheck = props.permits;
|
|
132
138
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
139
|
+
const permits = await Promise.allSettled(
|
|
140
|
+
permitToCheck.map((permit) =>
|
|
141
|
+
hasProjectPermit({ projectId: props.projectId, permit }, context)
|
|
142
|
+
)
|
|
143
|
+
);
|
|
137
144
|
|
|
138
|
-
|
|
139
|
-
|
|
145
|
+
for (const permit of permits) {
|
|
146
|
+
if (permit.status === "rejected") {
|
|
147
|
+
throw new Error(`Authorization call failed ${permit.reason}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (permit.value === true) {
|
|
151
|
+
return permitToCheck[permits.indexOf(permit)];
|
|
152
|
+
}
|
|
140
153
|
}
|
|
154
|
+
} finally {
|
|
155
|
+
const diff = Date.now() - start;
|
|
156
|
+
|
|
157
|
+
// eslint-disable-next-line no-console
|
|
158
|
+
console.log(`getProjectPermit execution ${diff}ms`);
|
|
141
159
|
}
|
|
142
160
|
};
|