5htp-core 0.1.2-3 → 0.2.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/changelog.md +5 -0
- package/doc/TODO.md +71 -0
- package/package.json +5 -4
- package/src/client/{App.tsx → app/component.tsx} +15 -8
- package/src/client/app/index.ts +128 -0
- package/src/client/app/service.ts +34 -0
- package/src/client/app.tsconfig.json +0 -4
- package/src/client/components/Card/index.tsx +1 -1
- package/src/client/components/Dialog/Manager.tsx +39 -12
- package/src/client/components/Form/index.tsx +1 -1
- package/src/client/components/button.tsx +2 -2
- package/src/client/components/containers/Popover/index.tsx +1 -1
- package/src/client/components/data/spintext/index.tsx +1 -1
- package/src/client/components/dropdown/index.tsx +1 -1
- package/src/client/components/index.ts +8 -0
- package/src/client/components/input/BaseV2/index.tsx +1 -1
- package/src/client/components/input/UploadImage/index.tsx +1 -1
- package/src/client/hooks/index.ts +5 -0
- package/src/client/hooks/useState/index.tsx +2 -2
- package/src/client/hooks.ts +22 -0
- package/src/client/index.ts +5 -0
- package/src/client/pages/_layout/landing/index.tsx +0 -2
- package/src/client/pages/_messages/400.tsx +2 -2
- package/src/client/pages/_messages/401.tsx +2 -2
- package/src/client/pages/_messages/403.tsx +2 -2
- package/src/client/pages/_messages/404.tsx +2 -2
- package/src/client/pages/_messages/500.tsx +2 -2
- package/src/client/pages/bug.tsx +1 -1
- package/src/client/pages/useHeader.tsx +1 -1
- package/src/client/{context/captcha.ts → services/captcha/index.ts} +0 -0
- package/src/client/services/metrics/index.ts +37 -0
- package/src/client/{router → services/router/components}/Link.tsx +1 -1
- package/src/client/services/router/components/Page.tsx +59 -0
- package/src/client/{router/component.tsx → services/router/components/router.tsx} +43 -74
- package/src/client/services/router/index.tsx +448 -0
- package/src/client/services/router/request/api.ts +229 -0
- package/src/client/{router → services/router}/request/history.ts +0 -0
- package/src/client/services/router/request/index.ts +52 -0
- package/src/client/services/router/response/index.tsx +107 -0
- package/src/client/services/router/response/page.ts +95 -0
- package/src/client/{context/socket.ts → services/socket/index.ts} +2 -2
- package/src/client/utils/dom.ts +1 -1
- package/src/common/app/index.ts +9 -0
- package/src/common/data/chaines/index.ts +9 -6
- package/src/common/data/input/validate.ts +3 -166
- package/src/common/data/objets.ts +25 -0
- package/src/common/data/tableaux.ts +8 -0
- package/src/common/errors/index.ts +3 -1
- package/src/common/router/index.ts +67 -88
- package/src/common/router/layouts.ts +50 -0
- package/src/common/router/register.ts +62 -0
- package/src/common/router/request/api.ts +72 -0
- package/src/common/router/request/index.ts +31 -0
- package/src/common/router/{response.ts → response/index.ts} +9 -13
- package/src/common/router/response/page.ts +40 -56
- package/src/common/validation/index.ts +3 -0
- package/src/common/validation/schema.ts +184 -0
- package/src/common/validation/validator.ts +88 -0
- package/src/common/validation/validators.ts +313 -0
- package/src/server/app/config.ts +8 -10
- package/src/server/app/index.ts +81 -124
- package/src/server/app/service.ts +98 -0
- package/src/server/app.tsconfig.json +0 -8
- package/src/server/error/index.ts +13 -0
- package/src/server/index.ts +5 -0
- package/src/server/patch.ts +0 -6
- package/src/server/{data/Cache.ts → services/cache/index.ts} +79 -47
- package/src/server/services/console/bugReporter.ts +26 -16
- package/src/server/services/console/index.ts +50 -51
- package/src/server/services/cron/index.ts +12 -26
- package/src/server/services/database/bucket.ts +40 -0
- package/src/server/services/database/connection.ts +206 -75
- package/src/server/services/database/datatypes.ts +63 -40
- package/src/server/services/database/index.ts +295 -272
- package/src/server/services/database/metas.ts +246 -135
- package/src/server/services/database/stats.ts +151 -126
- package/src/server/services/email/index.ts +24 -54
- package/src/server/services/{router/request/services → metrics}/detect.ts +6 -10
- package/src/server/services/{router/request/services/tracking.ts → metrics/index.ts} +68 -45
- package/src/server/services/{http → router/http}/index.ts +19 -47
- package/src/server/services/{http → router/http}/multipart.ts +0 -0
- package/src/server/services/{http → router/http}/session.ts.old +0 -0
- package/src/server/services/router/index.ts +273 -203
- package/src/server/services/router/request/api.ts +73 -0
- package/src/server/services/router/request/index.ts +16 -95
- package/src/server/services/router/request/service.ts +21 -0
- package/src/server/services/router/response/index.ts +125 -64
- package/src/server/services/router/response/{filter → mask}/Filter.ts +0 -0
- package/src/server/services/router/response/{filter → mask}/index.ts +0 -2
- package/src/server/services/router/response/{filter → mask}/selecteurs.ts +0 -0
- package/src/server/services/router/response/page/document.tsx +194 -0
- package/src/server/services/router/response/page/index.tsx +157 -0
- package/src/server/{libs/pages → services/router/response/page}/schemaGenerator.ts +0 -0
- package/src/server/services/router/service.ts +48 -0
- package/src/server/services/schema/index.ts +47 -0
- package/src/server/services/schema/request.ts +55 -0
- package/src/server/services/schema/router.ts +33 -0
- package/src/server/services/socket/index.ts +38 -43
- package/src/server/services/socket/scope.ts +6 -4
- package/src/server/services/users/index.ts +203 -0
- package/src/server/services/{auth/base.ts → users/old.ts} +20 -111
- package/src/server/services/users/router/index.ts +72 -0
- package/src/server/services/users/router/request.ts +49 -0
- package/src/types/aliases.d.ts +43 -2
- package/templates/composant.tsx +1 -1
- package/templates/modal.tsx +1 -1
- package/templates/page.tsx +1 -1
- package/tsconfig.common.json +0 -4
- package/src/client/context/api.ts +0 -92
- package/src/client/context/index.ts +0 -246
- package/src/client/index.tsx +0 -129
- package/src/client/router/index.ts +0 -286
- package/src/client/router/request/index.ts +0 -106
- package/src/client/router/response/index.ts +0 -38
- package/src/client/router/route.ts +0 -75
- package/src/common/data/input/validators/basic.ts +0 -299
- package/src/common/data/input/validators/build.ts +0 -63
- package/src/common/router/request.ts +0 -83
- package/src/server/data/ApiClient.ts +0 -119
- package/src/server/data/input.ts +0 -41
- package/src/server/libs/pages/document.static.tsx +0 -41
- package/src/server/libs/pages/document.tsx +0 -203
- package/src/server/libs/pages/render.tsx +0 -90
- package/src/server/routes/auth.ts +0 -151
- package/src/server/services/redis/index.ts +0 -71
- package/src/server/services/router/request/services/auth.ts +0 -177
|
@@ -7,7 +7,7 @@ import ws from 'ws';
|
|
|
7
7
|
import type { IncomingMessage } from 'http';
|
|
8
8
|
|
|
9
9
|
// Core
|
|
10
|
-
import
|
|
10
|
+
import type SocketService from '.';
|
|
11
11
|
import context from '@server/context';
|
|
12
12
|
|
|
13
13
|
/*----------------------------------
|
|
@@ -33,7 +33,7 @@ const activityDelay = 10 * 1000; // Clearn broken connections veery 10s
|
|
|
33
33
|
/*----------------------------------
|
|
34
34
|
- SCOPE
|
|
35
35
|
----------------------------------*/
|
|
36
|
-
export default class SocketScope {
|
|
36
|
+
export default class SocketScope<TUser extends {}> {
|
|
37
37
|
|
|
38
38
|
private connectEvent?: TConnectCallback;
|
|
39
39
|
private disconnectEvent?: TConnectCallback;
|
|
@@ -41,7 +41,9 @@ export default class SocketScope {
|
|
|
41
41
|
public users: { [username: string]: WebSocket[] } = {}
|
|
42
42
|
|
|
43
43
|
public constructor(
|
|
44
|
-
public path: string
|
|
44
|
+
public path: string,
|
|
45
|
+
private socket: SocketService<TUser>,
|
|
46
|
+
private app = socket.app
|
|
45
47
|
) {
|
|
46
48
|
|
|
47
49
|
}
|
|
@@ -74,7 +76,7 @@ export default class SocketScope {
|
|
|
74
76
|
context.run({ channelType: 'socket', channelId: this.path }, async () => {
|
|
75
77
|
|
|
76
78
|
// Auth
|
|
77
|
-
const username = await
|
|
79
|
+
const username = await this.socket.config.users.decode(req);
|
|
78
80
|
if (!username) {
|
|
79
81
|
console.log(`Rejecting connection on ${this.path} for client ${socket.ip} (${socket.id})}: Not authenticated`);
|
|
80
82
|
socket.close(4004, "auth");
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import jwt from 'jsonwebtoken';
|
|
7
|
+
import type express from 'express';
|
|
8
|
+
import type http from 'http';
|
|
9
|
+
|
|
10
|
+
// Core
|
|
11
|
+
import Application from '@server/app';
|
|
12
|
+
import Service from '@server/app/service';
|
|
13
|
+
import {
|
|
14
|
+
default as Router, Request as ServerRequest,
|
|
15
|
+
} from '@server/services/router';
|
|
16
|
+
import { InputError, AuthRequired, Forbidden } from '@common/errors';
|
|
17
|
+
|
|
18
|
+
/*----------------------------------
|
|
19
|
+
- TYPES
|
|
20
|
+
----------------------------------*/
|
|
21
|
+
|
|
22
|
+
export type TUserRole = typeof UserRoles[number]
|
|
23
|
+
|
|
24
|
+
export type THttpRequest = express.Request | http.IncomingMessage;
|
|
25
|
+
|
|
26
|
+
/*----------------------------------
|
|
27
|
+
- CONFIG
|
|
28
|
+
----------------------------------*/
|
|
29
|
+
|
|
30
|
+
const LogPrefix = '[auth]'
|
|
31
|
+
|
|
32
|
+
export const UserRoles = ['USER', 'ADMIN', 'TEST', 'DEV'] as const
|
|
33
|
+
|
|
34
|
+
/*----------------------------------
|
|
35
|
+
- SERVICE CONVIG
|
|
36
|
+
----------------------------------*/
|
|
37
|
+
|
|
38
|
+
export type TConfig = {
|
|
39
|
+
debug: boolean,
|
|
40
|
+
logoutUrl: string,
|
|
41
|
+
jwt: {
|
|
42
|
+
// 2048 bits
|
|
43
|
+
key: string,
|
|
44
|
+
expiration: string,
|
|
45
|
+
},
|
|
46
|
+
google?: {
|
|
47
|
+
web: {
|
|
48
|
+
clientId: string,
|
|
49
|
+
secret: string,
|
|
50
|
+
},
|
|
51
|
+
android: {
|
|
52
|
+
clientId: string
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type THooks = {
|
|
58
|
+
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/*----------------------------------
|
|
62
|
+
- SERVICE
|
|
63
|
+
----------------------------------*/
|
|
64
|
+
export default abstract class UsersManagementService<
|
|
65
|
+
TUser extends {},
|
|
66
|
+
TApplication extends Application,
|
|
67
|
+
TJwtSession extends {} = {},
|
|
68
|
+
TRequest extends ServerRequest<Router> = ServerRequest<Router>,
|
|
69
|
+
> extends Service<TConfig, THooks, TApplication> {
|
|
70
|
+
|
|
71
|
+
public async register() {
|
|
72
|
+
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public async start() {
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public abstract login( ...args: any[] ): Promise<{ user: TUser, token: string }>;
|
|
80
|
+
public abstract decodeSession( jwt: TJwtSession, req: THttpRequest ): Promise<TUser>;
|
|
81
|
+
|
|
82
|
+
protected abstract displayName(user: TUser): string;
|
|
83
|
+
protected abstract displaySessionName(session: TJwtSession): string;
|
|
84
|
+
|
|
85
|
+
public async decode( req: THttpRequest, withData: true ): Promise<TUser | null>;
|
|
86
|
+
public async decode( req: THttpRequest, withData?: false ): Promise<TJwtSession | null>;
|
|
87
|
+
public async decode( req: THttpRequest, withData: boolean = false ): Promise<TJwtSession | TUser | null> {
|
|
88
|
+
|
|
89
|
+
this.config.debug && console.log(LogPrefix, 'Decode:', { cookie: req.cookies['authorization'] });
|
|
90
|
+
|
|
91
|
+
let token: string | undefined;
|
|
92
|
+
if (('cookies' in req) && typeof req.cookies['authorization'] === 'string')
|
|
93
|
+
token = req.cookies['authorization'];
|
|
94
|
+
// Desktop app webview do not support cookie config, so wwe retrieve it from headers
|
|
95
|
+
else if (typeof req.headers['authorization'] === 'string')
|
|
96
|
+
token = req.headers['authorization'];
|
|
97
|
+
|
|
98
|
+
if (token === undefined)
|
|
99
|
+
return this.unauthorized(req);
|
|
100
|
+
|
|
101
|
+
let session: TJwtSession;
|
|
102
|
+
try {
|
|
103
|
+
session = jwt.verify(token, this.config.jwt.key, {
|
|
104
|
+
maxAge: this.config.jwt.expiration
|
|
105
|
+
});
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.warn(LogPrefix, "Failed to decode jwt token:", token);
|
|
108
|
+
return this.unauthorized(req);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Return email only
|
|
112
|
+
const sessionName = this.displaySessionName(session);
|
|
113
|
+
if (!withData) {
|
|
114
|
+
this.config.debug && console.log(LogPrefix, `Auth user ${sessionName} successfull. Return email only`);
|
|
115
|
+
return session;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Deserialize full user data
|
|
119
|
+
this.config.debug && console.log(LogPrefix, `Deserialize user ${sessionName}`);
|
|
120
|
+
const user = await this.decodeSession(session, req);
|
|
121
|
+
this.config.debug && console.log(LogPrefix, `Deserialized user ${sessionName}:`, user);
|
|
122
|
+
|
|
123
|
+
return user;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public unauthorized( req: THttpRequest ) {
|
|
127
|
+
|
|
128
|
+
if ('res' in req) {
|
|
129
|
+
// If use auth failed, we remove the jwt token so we avoid to trigger the same auth error in the next request
|
|
130
|
+
console.warn(LogPrefix, "Auth failed: remove authorization cookie");
|
|
131
|
+
req.res?.clearCookie('authorization');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
protected createSession( session: TJwtSession, request: TRequest ): string {
|
|
138
|
+
|
|
139
|
+
this.config.debug && console.info(LogPrefix, `Creating new session:`, session);
|
|
140
|
+
|
|
141
|
+
const token = jwt.sign(session, this.config.jwt.key);
|
|
142
|
+
|
|
143
|
+
this.config.debug && console.info(LogPrefix, `Generated JWT token for session:` + token);
|
|
144
|
+
|
|
145
|
+
request.res.cookie('authorization', token);
|
|
146
|
+
|
|
147
|
+
return token;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
public logout( request: TRequest ) {
|
|
151
|
+
|
|
152
|
+
const user = request.user;
|
|
153
|
+
if (!user) return;
|
|
154
|
+
|
|
155
|
+
this.config.debug && console.info(LogPrefix, `Logout ${this.displayName(user)}`);
|
|
156
|
+
request.res.clearCookie('authorization');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public check( request: TRequest, role: TUserRole, motivation?: string): TUser;
|
|
160
|
+
public check( request: TRequest, role: false, motivation?: string): null;
|
|
161
|
+
public check( request: TRequest, role: TUserRole | boolean = 'USER', motivation?: string): TUser | null {
|
|
162
|
+
|
|
163
|
+
const user = request.user;
|
|
164
|
+
|
|
165
|
+
this.config.debug && console.warn(LogPrefix, `Check auth, role = ${role}. Current user =`, user);
|
|
166
|
+
|
|
167
|
+
if (user === undefined) {
|
|
168
|
+
|
|
169
|
+
throw new Error(`request.user has not been decoded.`);
|
|
170
|
+
|
|
171
|
+
// Shortcut: { auth: true } <=> { auth: 'USER' }
|
|
172
|
+
} else if (role === true) {
|
|
173
|
+
|
|
174
|
+
role = 'USER';
|
|
175
|
+
|
|
176
|
+
// No auth needed
|
|
177
|
+
} else if (role === false) {
|
|
178
|
+
|
|
179
|
+
return user;
|
|
180
|
+
|
|
181
|
+
// Not connected
|
|
182
|
+
} else if (user === null) {
|
|
183
|
+
|
|
184
|
+
this.config.debug && console.warn(LogPrefix, "Refusé pour anonyme (" + request.ip + ")");
|
|
185
|
+
throw new AuthRequired(motivation);
|
|
186
|
+
|
|
187
|
+
// Insufficient permissions
|
|
188
|
+
} else if (!user.roles.includes(role)) {
|
|
189
|
+
|
|
190
|
+
console.warn(LogPrefix, "Refusé: " + role + " pour " + this.displayName(user) + " (" + (user.roles ? user.roles.join(', ') : 'role inconnu') + ")");
|
|
191
|
+
|
|
192
|
+
throw new Forbidden("You do not have sufficient permissions to access this resource.");
|
|
193
|
+
|
|
194
|
+
} else {
|
|
195
|
+
|
|
196
|
+
console.warn(LogPrefix, "Autorisé " + role + " pour " + this.displayName(user) + " (" + user.roles.join(', ') + ")");
|
|
197
|
+
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return user;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
}
|
|
@@ -1,28 +1,8 @@
|
|
|
1
|
-
/*----------------------------------
|
|
2
|
-
- DEPENDANCES
|
|
3
|
-
----------------------------------*/
|
|
4
1
|
|
|
5
|
-
// Npm
|
|
6
2
|
import md5 from 'md5';
|
|
7
3
|
import { OAuth2Client, LoginTicket } from 'google-auth-library';
|
|
8
4
|
|
|
9
|
-
// Core libs
|
|
10
|
-
import { Forbidden } from '@common/errors';
|
|
11
5
|
|
|
12
|
-
// App Libs
|
|
13
|
-
import { IP } from '@server/models';
|
|
14
|
-
import app, { $ } from '@server/app';
|
|
15
|
-
|
|
16
|
-
// Serbices
|
|
17
|
-
import '@server/services/database';
|
|
18
|
-
|
|
19
|
-
/*----------------------------------
|
|
20
|
-
- TYPES
|
|
21
|
-
----------------------------------*/
|
|
22
|
-
|
|
23
|
-
import type ServerRequest from '@server/services/router/request';
|
|
24
|
-
|
|
25
|
-
export type TUserRole = typeof UserRoles[number]
|
|
26
6
|
|
|
27
7
|
type AuthResponse = {
|
|
28
8
|
token: string,
|
|
@@ -30,96 +10,26 @@ type AuthResponse = {
|
|
|
30
10
|
user: User
|
|
31
11
|
}
|
|
32
12
|
|
|
33
|
-
|
|
34
|
-
- CONFIG
|
|
35
|
-
----------------------------------*/
|
|
36
|
-
|
|
37
|
-
const config = app.config.auth;
|
|
38
|
-
|
|
39
|
-
const LogPrefix = '[auth]'
|
|
40
|
-
|
|
41
|
-
export const UserRoles = ['USER', 'ADMIN', 'TEST', 'DEV'] as const
|
|
42
|
-
|
|
43
|
-
/*----------------------------------
|
|
44
|
-
- SERVICE CONVIG
|
|
45
|
-
----------------------------------*/
|
|
46
|
-
|
|
47
|
-
export type AuthConfig = {
|
|
48
|
-
debug: boolean,
|
|
49
|
-
logoutUrl: string,
|
|
50
|
-
jwt: {
|
|
51
|
-
// 2048 bits
|
|
52
|
-
key: string,
|
|
53
|
-
expiration: string,
|
|
54
|
-
},
|
|
55
|
-
google?: {
|
|
56
|
-
web: {
|
|
57
|
-
clientId: string,
|
|
58
|
-
secret: string,
|
|
59
|
-
},
|
|
60
|
-
android: {
|
|
61
|
-
clientId: string
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
declare global {
|
|
67
|
-
namespace Core {
|
|
68
|
-
|
|
69
|
-
interface Services {
|
|
70
|
-
auth: UserAuthBase
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
namespace Config {
|
|
74
|
-
interface Services {
|
|
75
|
-
auth: AuthConfig
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
13
|
+
export default class {
|
|
80
14
|
|
|
81
|
-
/*----------------------------------
|
|
82
|
-
- SERVICE
|
|
83
|
-
----------------------------------*/
|
|
84
|
-
export default abstract class UserAuthBase {
|
|
85
15
|
|
|
86
|
-
public
|
|
87
|
-
public abstract SsrMask: string;
|
|
88
|
-
|
|
89
|
-
public abstract beforeSignup(user: User): Promise< void >;
|
|
90
|
-
public abstract afterSignup(user: User): Promise<{ redirect: string }>;
|
|
91
|
-
|
|
92
|
-
private googleClient: OAuth2Client | undefined;
|
|
93
|
-
|
|
94
|
-
public async load() {
|
|
16
|
+
public async start() {
|
|
95
17
|
|
|
96
18
|
// Google auth client
|
|
97
|
-
if (
|
|
19
|
+
if (this.config.google) {
|
|
98
20
|
|
|
99
|
-
const httpConfig = app.
|
|
21
|
+
const httpConfig = this.app.http.publicUrl;
|
|
100
22
|
|
|
101
23
|
this.googleClient = new OAuth2Client(
|
|
102
|
-
|
|
103
|
-
|
|
24
|
+
this.config.google.web.clientId, // Google Client ID
|
|
25
|
+
this.config.google.web.secret, // Private key
|
|
104
26
|
httpConfig + "/auth/google/response" // Redirect url
|
|
105
27
|
);
|
|
106
28
|
}
|
|
107
|
-
|
|
108
29
|
}
|
|
109
30
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
request: ServerRequest,
|
|
113
|
-
userInfo: Partial<User> = {}
|
|
114
|
-
) {
|
|
115
|
-
|
|
116
|
-
const { token, user } = await this.Auth(email, request, false, userInfo);
|
|
117
|
-
|
|
118
|
-
return await request.response.json(
|
|
119
|
-
{ token, user },
|
|
120
|
-
`token user( ` + this.SsrMask + ` )`
|
|
121
|
-
)
|
|
122
|
-
}
|
|
31
|
+
|
|
32
|
+
private googleClient: OAuth2Client | undefined;
|
|
123
33
|
|
|
124
34
|
public async FromGoogle(request: ServerRequest): Promise<string> {
|
|
125
35
|
|
|
@@ -151,7 +61,7 @@ export default abstract class UserAuthBase {
|
|
|
151
61
|
request: ServerRequest,
|
|
152
62
|
): Promise<AuthResponse> {
|
|
153
63
|
|
|
154
|
-
const googleConfig =
|
|
64
|
+
const googleConfig = this.config.google;
|
|
155
65
|
if (!this.googleClient || !googleConfig)
|
|
156
66
|
throw new Forbidden(`Authentication method disabled.`);
|
|
157
67
|
|
|
@@ -163,7 +73,7 @@ export default abstract class UserAuthBase {
|
|
|
163
73
|
return this.GoogleResponse('token', r.tokens.id_token, request);
|
|
164
74
|
}
|
|
165
75
|
|
|
166
|
-
config.debug && console.log(LogPrefix, "Auth via google", googleConfig);
|
|
76
|
+
this.config.debug && console.log(LogPrefix, "Auth via google", googleConfig);
|
|
167
77
|
|
|
168
78
|
let ticket: LoginTicket;
|
|
169
79
|
try {
|
|
@@ -205,7 +115,7 @@ export default abstract class UserAuthBase {
|
|
|
205
115
|
userInfo: Partial<User> = {}
|
|
206
116
|
): Promise<AuthResponse> {
|
|
207
117
|
|
|
208
|
-
let user = await this.getData('email = ' +
|
|
118
|
+
let user = await this.getData('email = ' + this.sql.esc(email));
|
|
209
119
|
let ip: IP;
|
|
210
120
|
let redirect: string;
|
|
211
121
|
if (!user) { // Signup
|
|
@@ -232,7 +142,7 @@ export default abstract class UserAuthBase {
|
|
|
232
142
|
|
|
233
143
|
}
|
|
234
144
|
|
|
235
|
-
/*await
|
|
145
|
+
/*await this.sql`
|
|
236
146
|
INSERT INTO UserLogin SET
|
|
237
147
|
user = ${user.email},
|
|
238
148
|
date = NOW(),
|
|
@@ -251,7 +161,7 @@ export default abstract class UserAuthBase {
|
|
|
251
161
|
let username = email.split('@')[0];
|
|
252
162
|
|
|
253
163
|
// Prefix username if alreasy existing
|
|
254
|
-
const duplicates = await
|
|
164
|
+
const duplicates = await this.sql`
|
|
255
165
|
FROM User
|
|
256
166
|
WHERE name REGEXP CONCAT('^', ${username}, '[0-9]*$');
|
|
257
167
|
`.count();
|
|
@@ -275,12 +185,12 @@ export default abstract class UserAuthBase {
|
|
|
275
185
|
// Referrer
|
|
276
186
|
if (user.referrer !== undefined) {
|
|
277
187
|
|
|
278
|
-
const refExists = await
|
|
188
|
+
const refExists = await this.sql`FROM User WHERE name = ${user.referrer}`.exists();
|
|
279
189
|
if (!refExists)
|
|
280
190
|
user.referrer = undefined;
|
|
281
191
|
else {
|
|
282
192
|
|
|
283
|
-
await
|
|
193
|
+
await this.sql.upsert('UserStats', {
|
|
284
194
|
user: user.referrer,
|
|
285
195
|
date: new Date,
|
|
286
196
|
refSignups: 1
|
|
@@ -291,8 +201,8 @@ export default abstract class UserAuthBase {
|
|
|
291
201
|
}
|
|
292
202
|
|
|
293
203
|
// Create user
|
|
294
|
-
await
|
|
295
|
-
await
|
|
204
|
+
await this.sql.insert('User', user);
|
|
205
|
+
await this.sql.update('logs.IP', { user_name: username }, { address: request.ip });
|
|
296
206
|
|
|
297
207
|
// Hook
|
|
298
208
|
let redirect: string = '/';
|
|
@@ -303,7 +213,7 @@ export default abstract class UserAuthBase {
|
|
|
303
213
|
// remove user.emailHash
|
|
304
214
|
|
|
305
215
|
// Notif email
|
|
306
|
-
await
|
|
216
|
+
await this.email.send({
|
|
307
217
|
to: app.identity.author.email,
|
|
308
218
|
subject: app.identity.name + ": New User",
|
|
309
219
|
html: JSON.stringify(user)
|
|
@@ -342,7 +252,7 @@ export default abstract class UserAuthBase {
|
|
|
342
252
|
return;
|
|
343
253
|
|
|
344
254
|
// Check if user exists
|
|
345
|
-
const referrerExists = await
|
|
255
|
+
const referrerExists = await this.sql`FROM User WHERE name = ${referrer}`.count();
|
|
346
256
|
if (referrerExists === 0)
|
|
347
257
|
return;
|
|
348
258
|
|
|
@@ -354,7 +264,7 @@ export default abstract class UserAuthBase {
|
|
|
354
264
|
response.cookie('r', referrer);
|
|
355
265
|
|
|
356
266
|
// Count the clic
|
|
357
|
-
await
|
|
267
|
+
await this.sql.upsert('UserStats', {
|
|
358
268
|
user: referrer,
|
|
359
269
|
date: new Date,
|
|
360
270
|
refClics: 1
|
|
@@ -364,5 +274,4 @@ export default abstract class UserAuthBase {
|
|
|
364
274
|
}
|
|
365
275
|
|
|
366
276
|
}
|
|
367
|
-
|
|
368
277
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
|
|
7
|
+
// Core
|
|
8
|
+
import type Application from '@server/app';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
default as Router, Request as ServerRequest, TRoute,
|
|
12
|
+
RouterService
|
|
13
|
+
} from '@server/services/router';
|
|
14
|
+
|
|
15
|
+
// Specific
|
|
16
|
+
import type { default as UsersService, TUserRole } from '..';
|
|
17
|
+
import UsersRequestService from './request';
|
|
18
|
+
|
|
19
|
+
/*----------------------------------
|
|
20
|
+
- TYPES
|
|
21
|
+
----------------------------------*/
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
/*----------------------------------
|
|
25
|
+
- CONFIG
|
|
26
|
+
----------------------------------*/
|
|
27
|
+
|
|
28
|
+
const LogPrefix = '[router][auth]';
|
|
29
|
+
|
|
30
|
+
/*----------------------------------
|
|
31
|
+
- SERVICE
|
|
32
|
+
----------------------------------*/
|
|
33
|
+
export default class AuthenticationRouterService<
|
|
34
|
+
TUser extends {} = {},
|
|
35
|
+
TRequest extends ServerRequest<Router> = ServerRequest<Router>,
|
|
36
|
+
> extends RouterService {
|
|
37
|
+
|
|
38
|
+
public constructor(
|
|
39
|
+
public users: UsersService<TUser, Application>,
|
|
40
|
+
public config = users.config
|
|
41
|
+
) {
|
|
42
|
+
|
|
43
|
+
super();
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public async register() {
|
|
48
|
+
|
|
49
|
+
// Decode current user
|
|
50
|
+
this.router.on('request', async (request: TRequest) => {
|
|
51
|
+
|
|
52
|
+
// TODO: Typings. (context.user ?)
|
|
53
|
+
const decoded = await this.users.decode( request.req, true);
|
|
54
|
+
|
|
55
|
+
console.log("DECODED", decoded);
|
|
56
|
+
|
|
57
|
+
request.user = decoded || null;
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// Check route permissions
|
|
61
|
+
this.router.on('resolved', async (route: TRoute, request: TRequest) => {
|
|
62
|
+
|
|
63
|
+
if (route.options.auth !== undefined)
|
|
64
|
+
// TODO: How to pas the router type to router config ? Circular rfeerence ?
|
|
65
|
+
this.users.check(request, route.options.auth);
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public requestService( request: TRequest ): UsersRequestService<TUser> {
|
|
70
|
+
return new UsersRequestService( request, this );
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import jwt from 'jsonwebtoken';
|
|
7
|
+
|
|
8
|
+
// Core
|
|
9
|
+
import type { default as Router, Request as ServerRequest } from '@server/services/router';
|
|
10
|
+
import RequestService from '@server/services/router/request/service';
|
|
11
|
+
import { InputError, AuthRequired, Forbidden } from '@common/errors';
|
|
12
|
+
|
|
13
|
+
// Specific
|
|
14
|
+
import type AuthenticationRouterService from '.';
|
|
15
|
+
import type { default as UsersManagementService, TUserRole } from '..';
|
|
16
|
+
|
|
17
|
+
/*----------------------------------
|
|
18
|
+
- TYPES
|
|
19
|
+
----------------------------------*/
|
|
20
|
+
|
|
21
|
+
/*----------------------------------
|
|
22
|
+
- MODULE
|
|
23
|
+
----------------------------------*/
|
|
24
|
+
export default class UsersRequestService<
|
|
25
|
+
TUser extends {}
|
|
26
|
+
> extends RequestService {
|
|
27
|
+
|
|
28
|
+
public constructor(
|
|
29
|
+
request: ServerRequest<Router>,
|
|
30
|
+
public auth: AuthenticationRouterService,
|
|
31
|
+
public users = auth.users,
|
|
32
|
+
) {
|
|
33
|
+
super(request);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public login( email: string ) {
|
|
37
|
+
return this.users.login( this.request, email );
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public logout() {
|
|
41
|
+
return this.users.logout( this.request );
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public check( role: TUserRole, motivation?: string): TUser;
|
|
45
|
+
public check( role: false, motivation?: string): null;
|
|
46
|
+
public check( role: TUserRole | boolean = 'USER', motivation?: string): TUser | null {
|
|
47
|
+
return this.users.check( this.request, role, motivation );
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/types/aliases.d.ts
CHANGED
|
@@ -1,13 +1,54 @@
|
|
|
1
|
+
declare module "@/client/pages/\*\*/_layout/index.tsx" {
|
|
2
|
+
const Layout: import("../common/router/layouts").ImportedLayouts;
|
|
3
|
+
export = LayoutsList
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
declare module "@/client/pages/\*.tsx" {
|
|
2
|
-
const value: import("../client/router").TRoutesLoaders;
|
|
7
|
+
const value: import("../client/services/router").TRoutesLoaders;
|
|
3
8
|
export = value;
|
|
4
9
|
}
|
|
10
|
+
|
|
5
11
|
declare module "@client/pages/\*.tsx" {
|
|
6
|
-
const value: import("../client/router").TRoutesLoaders;
|
|
12
|
+
const value: import("../client/services/router").TRoutesLoaders;
|
|
7
13
|
export = value;
|
|
8
14
|
}
|
|
9
15
|
|
|
10
16
|
declare module "@/server/services/auth" {
|
|
11
17
|
const UserAuthService: import("../server/services/auth/base").default;
|
|
12
18
|
export = UserAuthService;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare module "@/server" {
|
|
22
|
+
const ServerApplicationClass: import("../server/app").default;
|
|
23
|
+
export = InstanceType<ServerApplicationClass>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
declare module "@/client" {
|
|
27
|
+
const ClientApplicationClass: import("../client/app").default;
|
|
28
|
+
export = InstanceType<ClientApplicationClass>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
declare module "@/client/context" {
|
|
32
|
+
|
|
33
|
+
const Test: true;
|
|
34
|
+
|
|
35
|
+
const ClientRouter: import('../client/services/router').default;
|
|
36
|
+
const ServerRouter: import('../server/services/router').default;
|
|
37
|
+
|
|
38
|
+
const TServerRouterRequestContext: import('../server/services/router/response').TRouterContext;
|
|
39
|
+
const TClientRouterRequestContext: import('../client/services/router/response').TRouterContext;
|
|
40
|
+
|
|
41
|
+
export const ClientContext = (
|
|
42
|
+
// TO Fix: TClientRouterRequestContext is unable to get the right type of CrossPathClient["router"]
|
|
43
|
+
// (it gets ClientApplication instead of CrossPathClient)
|
|
44
|
+
TClientRouterRequestContext<ClientRouter, ClientRouter["app"]>
|
|
45
|
+
|
|
|
46
|
+
TServerRouterRequestContext<ServerRouter>
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
export const ReactClientContext: preact.Context<ClientContext>;
|
|
50
|
+
|
|
51
|
+
const useContext: () => ClientContext;
|
|
52
|
+
|
|
53
|
+
export = useContext;
|
|
13
54
|
}
|
package/templates/composant.tsx
CHANGED
package/templates/modal.tsx
CHANGED
package/templates/page.tsx
CHANGED