prostgles-server 4.2.269 → 4.2.271

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.
Files changed (52) hide show
  1. package/dist/Auth/AuthHandler.d.ts +7 -6
  2. package/dist/Auth/AuthHandler.d.ts.map +1 -1
  3. package/dist/Auth/AuthHandler.js +22 -52
  4. package/dist/Auth/AuthHandler.js.map +1 -1
  5. package/dist/Auth/AuthTypes.d.ts +7 -1
  6. package/dist/Auth/AuthTypes.d.ts.map +1 -1
  7. package/dist/Auth/AuthTypes.js.map +1 -1
  8. package/dist/Auth/endpoints/setCatchAllRequestHandler.js +2 -2
  9. package/dist/Auth/endpoints/setCatchAllRequestHandler.js.map +1 -1
  10. package/dist/Auth/getClientAuth.js +2 -3
  11. package/dist/Auth/getClientAuth.js.map +1 -1
  12. package/dist/Auth/setupAuthRoutes.d.ts.map +1 -1
  13. package/dist/Auth/setupAuthRoutes.js +2 -1
  14. package/dist/Auth/setupAuthRoutes.js.map +1 -1
  15. package/dist/Auth/utils/getSidAndUserFromRequest.d.ts +1 -0
  16. package/dist/Auth/utils/getSidAndUserFromRequest.d.ts.map +1 -1
  17. package/dist/Auth/utils/getSidAndUserFromRequest.js +38 -24
  18. package/dist/Auth/utils/getSidAndUserFromRequest.js.map +1 -1
  19. package/dist/Auth/utils/getUserOrError.d.ts +9 -0
  20. package/dist/Auth/utils/getUserOrError.d.ts.map +1 -0
  21. package/dist/Auth/utils/getUserOrError.js +48 -0
  22. package/dist/Auth/utils/getUserOrError.js.map +1 -0
  23. package/dist/Auth/utils/handleGetUser.d.ts +6 -0
  24. package/dist/Auth/utils/handleGetUser.d.ts.map +1 -0
  25. package/dist/Auth/utils/handleGetUser.js +53 -0
  26. package/dist/Auth/utils/handleGetUser.js.map +1 -0
  27. package/dist/DboBuilder/DboBuilderTypes.d.ts +2 -1
  28. package/dist/DboBuilder/DboBuilderTypes.d.ts.map +1 -1
  29. package/dist/Prostgles.d.ts.map +1 -1
  30. package/dist/Prostgles.js.map +1 -1
  31. package/dist/PublishParser/PublishParser.d.ts.map +1 -1
  32. package/dist/PublishParser/PublishParser.js +3 -4
  33. package/dist/PublishParser/PublishParser.js.map +1 -1
  34. package/dist/PublishParser/getSchemaFromPublish.d.ts.map +1 -1
  35. package/dist/PublishParser/getSchemaFromPublish.js +67 -69
  36. package/dist/PublishParser/getSchemaFromPublish.js.map +1 -1
  37. package/dist/onSocketConnected.js +3 -3
  38. package/dist/onSocketConnected.js.map +1 -1
  39. package/lib/Auth/AuthHandler.ts +25 -76
  40. package/lib/Auth/AuthTypes.ts +9 -1
  41. package/lib/Auth/endpoints/setCatchAllRequestHandler.ts +2 -2
  42. package/lib/Auth/getClientAuth.ts +10 -10
  43. package/lib/Auth/setupAuthRoutes.ts +2 -1
  44. package/lib/Auth/utils/getSidAndUserFromRequest.ts +38 -31
  45. package/lib/Auth/utils/getUserOrError.ts +56 -0
  46. package/lib/Auth/utils/handleGetUser.ts +67 -0
  47. package/lib/DboBuilder/DboBuilderTypes.ts +2 -1
  48. package/lib/Prostgles.ts +1 -0
  49. package/lib/PublishParser/PublishParser.ts +4 -3
  50. package/lib/PublishParser/getSchemaFromPublish.ts +96 -95
  51. package/lib/onSocketConnected.ts +3 -3
  52. package/package.json +2 -2
@@ -1,16 +1,8 @@
1
- import { AnyObject, AuthResponse, CHANNELS } from "prostgles-types";
1
+ import { AnyObject, CHANNELS } from "prostgles-types";
2
2
  import { PRGLIOSocket } from "../DboBuilder/DboBuilder";
3
- import { DBOFullyTyped } from "../DBSchemaBuilder";
4
3
  import { removeExpressRoute } from "../FileManager/FileManager";
5
4
  import { DB, DBHandlerServer, Prostgles } from "../Prostgles";
6
- import {
7
- AuthClientRequest,
8
- AuthConfig,
9
- AuthResult,
10
- AuthResultWithSID,
11
- BasicSession,
12
- ExpressReq,
13
- } from "./AuthTypes";
5
+ import { AuthClientRequest, AuthConfig, AuthResult, BasicSession, ExpressReq } from "./AuthTypes";
14
6
  import { LoginResponseHandler } from "./endpoints/setLoginRequestHandler";
15
7
  import { getClientAuth } from "./getClientAuth";
16
8
  import { login } from "./login";
@@ -18,7 +10,8 @@ import { setupAuthRoutes } from "./setupAuthRoutes";
18
10
  import { getClientRequestIPsInfo } from "./utils/getClientRequestIPsInfo";
19
11
  import { getReturnUrl } from "./utils/getReturnUrl";
20
12
  import { getSidAndUserFromRequest } from "./utils/getSidAndUserFromRequest";
21
- import { throttledAuthCall } from "./utils/throttledReject";
13
+ import { getUserOrError } from "./utils/getUserOrError";
14
+ import { handleGetUserThrottled } from "./utils/handleGetUser";
22
15
 
23
16
  export { getClientRequestIPsInfo };
24
17
  export const HTTP_FAIL_CODES = {
@@ -130,51 +123,8 @@ export class AuthHandler {
130
123
  const successURL = getReturnUrl(req) || "/";
131
124
  res.redirect(successURL);
132
125
  };
133
-
134
- getUserOrError = async (localParams: AuthClientRequest): Promise<AuthResultWithSID> => {
135
- const sid = this.getSID(localParams);
136
- if (!sid) return { sid };
137
-
138
- const isError = (
139
- dataOrError: any
140
- ): dataOrError is AuthResponse.AuthFailure["code"] | AuthResponse.AuthFailure => {
141
- return Boolean(typeof dataOrError === "string" || (dataOrError && "success" in dataOrError));
142
- };
143
- try {
144
- const userOrErrorCode = await throttledAuthCall(async () => {
145
- return this.opts.getUser(
146
- this.validateSid(sid),
147
- this.dbo as DBOFullyTyped,
148
- this.db,
149
- getClientRequestIPsInfo(localParams),
150
- localParams
151
- );
152
- }, 50);
153
-
154
- if (isError(userOrErrorCode)) {
155
- const error: AuthResponse.AuthFailure | undefined =
156
- typeof userOrErrorCode === "string" ?
157
- { success: false, code: userOrErrorCode }
158
- : userOrErrorCode;
159
-
160
- return {
161
- sid,
162
- error,
163
- };
164
- }
165
- if (sid && userOrErrorCode?.user) {
166
- return { sid, ...userOrErrorCode };
167
- }
168
- return {
169
- sid,
170
- };
171
- } catch (_err) {
172
- return {
173
- sid,
174
- error: { success: false, code: "server-error" },
175
- };
176
- }
177
- };
126
+ handleGetUser = handleGetUserThrottled.bind(this);
127
+ getUserOrError = getUserOrError.bind(this);
178
128
 
179
129
  init = setupAuthRoutes.bind(this);
180
130
 
@@ -210,7 +160,7 @@ export class AuthHandler {
210
160
  * - query params
211
161
  * Based on sidKeyName from auth
212
162
  */
213
- getSID(maybeClientReq: AuthClientRequest | undefined): string | undefined {
163
+ getValidatedSid(maybeClientReq: AuthClientRequest | undefined): string | undefined {
214
164
  if (!maybeClientReq) return undefined;
215
165
  const { sidKeyName } = this;
216
166
  if (maybeClientReq.socket) {
@@ -225,18 +175,20 @@ export class AuthHandler {
225
175
  rawSid = cookie[sidKeyName];
226
176
  }
227
177
  return this.validateSid(rawSid);
228
- } else {
229
- const [tokenType, base64Token] =
230
- maybeClientReq.httpReq.headers.authorization?.split(" ") ?? [];
231
- let bearerSid: string | undefined;
232
- if (tokenType && base64Token) {
233
- if (tokenType.trim() !== "Bearer") {
234
- throw "Only Bearer Authorization header allowed";
235
- }
236
- bearerSid = Buffer.from(base64Token, "base64").toString();
178
+ }
179
+
180
+ const [tokenType, base64Token] = maybeClientReq.httpReq.headers.authorization?.split(" ") ?? [];
181
+ let bearerSid: string | undefined;
182
+ if (tokenType && base64Token) {
183
+ if (tokenType.trim() !== "Bearer") {
184
+ throw "Only Bearer Authorization header allowed";
237
185
  }
238
- return this.validateSid(bearerSid ?? maybeClientReq.httpReq.cookies?.[sidKeyName]);
186
+ bearerSid = Buffer.from(base64Token, "base64").toString();
239
187
  }
188
+ return this.validateSid(
189
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
190
+ bearerSid ?? (maybeClientReq.httpReq.cookies?.[sidKeyName] as string | undefined)
191
+ );
240
192
  }
241
193
 
242
194
  /**
@@ -245,7 +197,7 @@ export class AuthHandler {
245
197
  getSIDNoError = (clientReq: AuthClientRequest | undefined): string | undefined => {
246
198
  if (!clientReq) return undefined;
247
199
  try {
248
- return this.getSID(clientReq);
200
+ return this.getValidatedSid(clientReq);
249
201
  } catch {
250
202
  return undefined;
251
203
  }
@@ -284,14 +236,11 @@ export class AuthHandler {
284
236
  getClientAuth = getClientAuth.bind(this);
285
237
  }
286
238
 
287
- export const matchesRoute = (shorterRoute: string | undefined, longerRoute: string) => {
288
- return (
289
- shorterRoute &&
290
- longerRoute &&
291
- (shorterRoute === longerRoute ||
292
- (longerRoute.startsWith(shorterRoute) &&
293
- ["/", "?", "#"].includes(longerRoute[shorterRoute.length] ?? "")))
294
- );
239
+ export const matchesRoute = (baseRoute: string | undefined, fullRoute: string) => {
240
+ if (!baseRoute || !fullRoute) return false;
241
+ if (baseRoute === fullRoute) return true;
242
+ const nextChar = fullRoute[baseRoute.length] ?? "";
243
+ return fullRoute.startsWith(baseRoute) && ["/", "?", "#"].includes(nextChar);
295
244
  };
296
245
 
297
246
  const parseCookieStr = (cookie_str: string | undefined): Record<string, string> => {
@@ -273,7 +273,15 @@ export type AuthResult<SU = SessionUser> =
273
273
  export type AuthResultOrError<SU = SessionUser> =
274
274
  | AuthResponse.AuthFailure
275
275
  | AuthResponse.AuthFailure["code"]
276
- | AuthResult<SU>;
276
+ | AuthResult<SU>
277
+ | {
278
+ type: "new-session";
279
+
280
+ /**
281
+ * If provided must login the user. Used for passwordless admin and public users
282
+ */
283
+ session: BasicSession;
284
+ };
277
285
 
278
286
  export type AuthRequestParams<S, SUser extends SessionUser> = {
279
287
  db: DB;
@@ -38,8 +38,8 @@ export function setCatchAllRequestHandler(this: AuthHandler, app: e.Express) {
38
38
  */
39
39
  if (this.isUserRoute(req.path)) {
40
40
  /* Check auth. Redirect to login if unauthorized */
41
- const u = await isLoggedInUser();
42
- if (!u) {
41
+ const isLoggedIn = await isLoggedInUser();
42
+ if (!isLoggedIn) {
43
43
  res.redirect(
44
44
  `${AUTH_ROUTES_AND_PARAMS.login}?returnURL=${encodeURIComponent(req.originalUrl)}`
45
45
  );
@@ -14,10 +14,16 @@ export async function getClientAuth(
14
14
  clientReq: AuthClientRequest
15
15
  ): Promise<{ auth: AuthSocketSchema; userData: AuthResultWithSID }> {
16
16
  let pathGuard = false;
17
- if (
18
- this.opts.loginSignupConfig?.publicRoutes &&
19
- !this.opts.loginSignupConfig.disableSocketAuthGuard
20
- ) {
17
+ const {
18
+ loginWithOAuth,
19
+ signupWithEmail: signupWithEmailAndPassword,
20
+ localLoginMode,
21
+ login,
22
+ publicRoutes,
23
+ disableSocketAuthGuard,
24
+ } = this.opts.loginSignupConfig ?? {};
25
+
26
+ if (publicRoutes && !disableSocketAuthGuard) {
21
27
  pathGuard = true;
22
28
 
23
29
  /**
@@ -64,12 +70,6 @@ export async function getClientAuth(
64
70
  }
65
71
 
66
72
  const userData = await this.getSidAndUserFromRequest(clientReq);
67
- const {
68
- loginWithOAuth,
69
- signupWithEmail: signupWithEmailAndPassword,
70
- localLoginMode,
71
- login,
72
- } = this.opts.loginSignupConfig ?? {};
73
73
 
74
74
  const auth: AuthSocketSchema = {
75
75
  providers: getOAuthProviders(loginWithOAuth),
@@ -49,7 +49,8 @@ export function setupAuthRoutes(this: AuthHandler) {
49
49
  );
50
50
 
51
51
  if (errorInfo) {
52
- res.status(HTTP_FAIL_CODES.BAD_REQUEST).json(errorInfo);
52
+ const { error, httpCode } = errorInfo;
53
+ res.status(httpCode).json({ error });
53
54
  return;
54
55
  }
55
56
  next();
@@ -6,6 +6,7 @@ import { throttledAuthCall } from "./throttledReject";
6
6
  /**
7
7
  * For a given sid return the user data if available using the auth handler's getUser method.
8
8
  * Use socket session cache if configured in Auth
9
+ * Used in Publish Parser and AuthHandler
9
10
  */
10
11
  export async function getSidAndUserFromRequest(
11
12
  this: AuthHandler,
@@ -16,7 +17,7 @@ export async function getSidAndUserFromRequest(
16
17
  */
17
18
  const getSessionForCaching = this.opts.cacheSession?.getSession;
18
19
  if (clientReq.socket && getSessionForCaching && clientReq.socket.__prglCache) {
19
- const { session, ...userData } = clientReq.socket.__prglCache;
20
+ const { session, userData } = clientReq.socket.__prglCache;
20
21
  const isValid = this.isNonExpiredSocketSession(clientReq.socket, session);
21
22
  if (isValid) {
22
23
  return {
@@ -33,40 +34,46 @@ export async function getSidAndUserFromRequest(
33
34
  * Get sid from request and fetch user data
34
35
  */
35
36
  const authStart = Date.now();
36
- const result = await throttledAuthCall(async () => {
37
- const { getUser } = this.opts;
37
+ // const result = await throttledAuthCall(async () => {
38
+ // const clientInfoOrErr = await this.opts.getUser(
39
+ // this.getValidatedSid(clientReq),
40
+ // this.dbo as DBOFullyTyped,
41
+ // this.db,
42
+ // getClientRequestIPsInfo(clientReq),
43
+ // clientReq
44
+ // );
45
+ // if (clientInfoOrErr && (typeof clientInfoOrErr === "string" || "success" in clientInfoOrErr))
46
+ // throw clientInfoOrErr;
47
+ // const clientInfo = clientInfoOrErr;
38
48
 
39
- const sid = this.getSID(clientReq);
40
- const clientInfoOrErr =
41
- !sid ? undefined : (
42
- await getUser(
43
- sid,
44
- this.dbo as DBOFullyTyped,
45
- this.db,
46
- getClientRequestIPsInfo(clientReq),
47
- clientReq
48
- )
49
- );
50
- if (clientInfoOrErr && (typeof clientInfoOrErr === "string" || "success" in clientInfoOrErr))
51
- throw clientInfoOrErr;
52
- const clientInfo = clientInfoOrErr;
53
- if (getSessionForCaching && clientReq.socket && sid) {
54
- const session = await getSessionForCaching(sid, this.dbo as DBOFullyTyped, this.db);
55
- if (session && session.expires && clientInfo?.user) {
56
- clientReq.socket.__prglCache = {
57
- ...clientInfo,
58
- session,
59
- };
60
- }
61
- }
49
+ // if (clientInfo && "type" in clientInfo) {
50
+ // if (!("httpReq" in clientReq) || !clientReq.httpReq) throw "httpReq missing";
51
+ // const { httpReq, res } = clientReq;
52
+ // this.setCookieAndGoToReturnURLIFSet(clientInfo.session, { req: httpReq, res });
53
+ // return;
54
+ // }
62
55
 
63
- if (clientInfo?.user && sid) {
64
- return { sid, ...clientInfo };
65
- }
56
+ // const sid = this.getValidatedSid(clientReq);
57
+ // if (getSessionForCaching && clientReq.socket && sid) {
58
+ // const session = await getSessionForCaching(sid, this.dbo as DBOFullyTyped, this.db);
59
+ // if (session && session.expires && clientInfo?.user) {
60
+ // clientReq.socket.__prglCache = {
61
+ // userData: clientInfo,
62
+ // session,
63
+ // };
64
+ // }
65
+ // }
66
66
 
67
- return { sid, preferredLogin: !clientInfo?.user ? clientInfo?.preferredLogin : undefined };
68
- }, 100);
67
+ // if (clientInfo?.user && sid) {
68
+ // return { sid, ...clientInfo };
69
+ // }
69
70
 
71
+ // return { sid, preferredLogin: !clientInfo?.user ? clientInfo?.preferredLogin : undefined };
72
+ // }, 100);
73
+ const result = await this.handleGetUser(clientReq);
74
+ if (result.error) {
75
+ throw result.error;
76
+ }
70
77
  await this.prostgles.opts.onLog?.({
71
78
  type: "auth",
72
79
  command: "getClientInfo",
@@ -0,0 +1,56 @@
1
+ import { AuthResponse } from "prostgles-types";
2
+ import { DBOFullyTyped } from "../../DBSchemaBuilder";
3
+ import type { AuthHandler } from "../AuthHandler";
4
+ import { AuthClientRequest, AuthResultWithSID } from "../AuthTypes";
5
+ import { getClientRequestIPsInfo } from "../utils/getClientRequestIPsInfo";
6
+ import { isAuthError } from "./handleGetUser";
7
+ import { throttledAuthCall } from "./throttledReject";
8
+
9
+ /**
10
+ * Used by:
11
+ * - setCatchAllRequestHandler
12
+ * - loginSignupConfig.use
13
+ */
14
+ export async function getUserOrError(
15
+ this: AuthHandler,
16
+ clientReq: AuthClientRequest
17
+ ): Promise<AuthResultWithSID> {
18
+ // const sid = this.getValidatedSid(clientReq);
19
+ // if (!sid) return { sid };
20
+
21
+ try {
22
+ // const userOrErrorCode = await throttledAuthCall(async () => {
23
+ // return this.opts.getUser(
24
+ // this.validateSid(sid),
25
+ // this.dbo as DBOFullyTyped,
26
+ // this.db,
27
+ // getClientRequestIPsInfo(clientReq),
28
+ // clientReq
29
+ // );
30
+ // }, 50);
31
+
32
+ // if (isAuthError(userOrErrorCode)) {
33
+ // const error: AuthResponse.AuthFailure | undefined =
34
+ // typeof userOrErrorCode === "string" ?
35
+ // { success: false, code: userOrErrorCode }
36
+ // : userOrErrorCode;
37
+
38
+ // return {
39
+ // sid,
40
+ // error,
41
+ // };
42
+ // }
43
+ // if (sid && userOrErrorCode?.user) {
44
+ // return { sid, ...userOrErrorCode };
45
+ // }
46
+ // return {
47
+ // sid,
48
+ // };
49
+ return this.handleGetUser(clientReq);
50
+ } catch (_err) {
51
+ return {
52
+ sid: this.getValidatedSid(clientReq),
53
+ error: { success: false, code: "server-error" },
54
+ };
55
+ }
56
+ }
@@ -0,0 +1,67 @@
1
+ import type { DBOFullyTyped } from "../../DBSchemaBuilder";
2
+ import { tout } from "../../PubSubManager/initPubSubManager";
3
+ import { getClientRequestIPsInfo, type AuthHandler } from "../AuthHandler";
4
+ import type { AuthClientRequest, AuthResultOrError, AuthResultWithSID } from "../AuthTypes";
5
+ import { throttledAuthCall } from "./throttledReject";
6
+ import { AuthResponse, isObject } from "prostgles-types";
7
+
8
+ export async function handleGetUserThrottled(
9
+ this: AuthHandler,
10
+ clientReq: AuthClientRequest
11
+ ): Promise<AuthResultWithSID> {
12
+ const getSessionForCaching = this.opts.cacheSession?.getSession;
13
+ const result = await throttledAuthCall(async () => {
14
+ const clientInfoOrErr = await this.opts.getUser(
15
+ this.getValidatedSid(clientReq),
16
+ this.dbo as DBOFullyTyped,
17
+ this.db,
18
+ getClientRequestIPsInfo(clientReq),
19
+ clientReq
20
+ );
21
+ if (isAuthError(clientInfoOrErr)) {
22
+ return {
23
+ error:
24
+ isObject(clientInfoOrErr) ? clientInfoOrErr : { success: false, code: clientInfoOrErr },
25
+ sid: this.getValidatedSid(clientReq),
26
+ } satisfies AuthResultWithSID;
27
+ }
28
+ const clientInfo = clientInfoOrErr;
29
+
30
+ if (clientInfo && "type" in clientInfo) {
31
+ if (!("httpReq" in clientReq) || !clientReq.httpReq)
32
+ throw "httpReq missing. new-session not implemented for sockets.";
33
+ const { httpReq, res } = clientReq;
34
+ this.setCookieAndGoToReturnURLIFSet(clientInfo.session, { req: httpReq, res });
35
+ /** Wait for refresh */
36
+ await tout(200);
37
+ return {
38
+ error: { success: false, code: "something-went-wrong" },
39
+ sid: this.getValidatedSid(clientReq),
40
+ } satisfies AuthResultWithSID;
41
+ }
42
+
43
+ const sid = this.getValidatedSid(clientReq);
44
+ if (getSessionForCaching && clientReq.socket && sid) {
45
+ const session = await getSessionForCaching(sid, this.dbo as DBOFullyTyped, this.db);
46
+ if (session && session.expires && clientInfo?.user) {
47
+ clientReq.socket.__prglCache = {
48
+ userData: clientInfo,
49
+ session,
50
+ };
51
+ }
52
+ }
53
+
54
+ if (clientInfo?.user && sid) {
55
+ return { sid, ...clientInfo };
56
+ }
57
+
58
+ return { sid, preferredLogin: !clientInfo?.user ? clientInfo?.preferredLogin : undefined };
59
+ }, 100);
60
+ return result;
61
+ }
62
+
63
+ export const isAuthError = (
64
+ dataOrError: AuthResultOrError
65
+ ): dataOrError is AuthResponse.AuthFailure["code"] | AuthResponse.AuthFailure => {
66
+ return Boolean(typeof dataOrError === "string" || (dataOrError && "success" in dataOrError));
67
+ };
@@ -168,7 +168,8 @@ export type PRGLIOSocket = {
168
168
  };
169
169
 
170
170
  /** Used for session caching */
171
- __prglCache?: SessionUser & {
171
+ __prglCache?: {
172
+ userData: Omit<SessionUser, "session">;
172
173
  session: BasicSession;
173
174
  };
174
175
 
package/lib/Prostgles.ts CHANGED
@@ -32,6 +32,7 @@ import {
32
32
  CHANNELS,
33
33
  ClientSchema,
34
34
  SQLRequest,
35
+ includes,
35
36
  isObject,
36
37
  omitKeys,
37
38
  tryCatchV2,
@@ -159,13 +159,14 @@ export class PublishParser {
159
159
  }
160
160
  }
161
161
 
162
- if (tableRule[rtm.rule]) {
163
- return tableRule;
164
- } else
162
+ if (!tableRule[rtm.rule]) {
165
163
  throw {
166
164
  stack: ["getValidatedRequestRule()"],
167
165
  message: `Invalid or disallowed command: ${tableName}.${command}`,
168
166
  };
167
+ }
168
+
169
+ return tableRule;
169
170
  }
170
171
 
171
172
  async getTableRules(