5htp-core 0.1.2 → 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/assets/css/medias.less +14 -0
- package/src/client/components/Card/index.tsx +2 -2
- 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 +9 -27
- 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 +59 -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 +28 -52
- package/src/server/services/{router/request/services → metrics}/detect.ts +8 -10
- package/src/server/services/{router/request/services/tracking.ts → metrics/index.ts} +68 -45
- package/src/server/services/{http → router/http}/index.ts +28 -70
- 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 -97
- 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} +28 -112
- 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,90 +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;
|
|
16
|
+
public async start() {
|
|
88
17
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
private googleClient = app.config.auth.google
|
|
93
|
-
? new OAuth2Client(
|
|
94
|
-
app.config.auth.google.web.clientId, // Google Client ID
|
|
95
|
-
app.config.auth.google.web.secret, // Private key
|
|
96
|
-
"https://" + app.env.domain + "/auth/google/response" // Redirect url
|
|
97
|
-
)
|
|
98
|
-
: undefined;
|
|
18
|
+
// Google auth client
|
|
19
|
+
if (this.config.google) {
|
|
99
20
|
|
|
100
|
-
|
|
21
|
+
const httpConfig = this.app.http.publicUrl;
|
|
101
22
|
|
|
23
|
+
this.googleClient = new OAuth2Client(
|
|
24
|
+
this.config.google.web.clientId, // Google Client ID
|
|
25
|
+
this.config.google.web.secret, // Private key
|
|
26
|
+
httpConfig + "/auth/google/response" // Redirect url
|
|
27
|
+
);
|
|
28
|
+
}
|
|
102
29
|
}
|
|
103
30
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
request: ServerRequest,
|
|
107
|
-
userInfo: Partial<User> = {}
|
|
108
|
-
) {
|
|
109
|
-
|
|
110
|
-
const { token, user } = await this.Auth(email, request, false, userInfo);
|
|
111
|
-
|
|
112
|
-
return await request.response.json(
|
|
113
|
-
{ token, user },
|
|
114
|
-
`token user( ` + this.SsrMask + ` )`
|
|
115
|
-
)
|
|
116
|
-
}
|
|
31
|
+
|
|
32
|
+
private googleClient: OAuth2Client | undefined;
|
|
117
33
|
|
|
118
34
|
public async FromGoogle(request: ServerRequest): Promise<string> {
|
|
119
35
|
|
|
@@ -145,7 +61,8 @@ export default abstract class UserAuthBase {
|
|
|
145
61
|
request: ServerRequest,
|
|
146
62
|
): Promise<AuthResponse> {
|
|
147
63
|
|
|
148
|
-
|
|
64
|
+
const googleConfig = this.config.google;
|
|
65
|
+
if (!this.googleClient || !googleConfig)
|
|
149
66
|
throw new Forbidden(`Authentication method disabled.`);
|
|
150
67
|
|
|
151
68
|
if (codeOrToken === undefined)
|
|
@@ -156,15 +73,15 @@ export default abstract class UserAuthBase {
|
|
|
156
73
|
return this.GoogleResponse('token', r.tokens.id_token, request);
|
|
157
74
|
}
|
|
158
75
|
|
|
159
|
-
config.debug && console.log(LogPrefix, "Auth via google",
|
|
76
|
+
this.config.debug && console.log(LogPrefix, "Auth via google", googleConfig);
|
|
160
77
|
|
|
161
78
|
let ticket: LoginTicket;
|
|
162
79
|
try {
|
|
163
80
|
ticket = await this.googleClient.verifyIdToken({
|
|
164
81
|
idToken: codeOrToken,
|
|
165
82
|
audience: [
|
|
166
|
-
|
|
167
|
-
|
|
83
|
+
googleConfig.web.clientId,
|
|
84
|
+
googleConfig.android.clientId,
|
|
168
85
|
]
|
|
169
86
|
});
|
|
170
87
|
} catch (error) {
|
|
@@ -198,7 +115,7 @@ export default abstract class UserAuthBase {
|
|
|
198
115
|
userInfo: Partial<User> = {}
|
|
199
116
|
): Promise<AuthResponse> {
|
|
200
117
|
|
|
201
|
-
let user = await this.getData('email = ' +
|
|
118
|
+
let user = await this.getData('email = ' + this.sql.esc(email));
|
|
202
119
|
let ip: IP;
|
|
203
120
|
let redirect: string;
|
|
204
121
|
if (!user) { // Signup
|
|
@@ -225,7 +142,7 @@ export default abstract class UserAuthBase {
|
|
|
225
142
|
|
|
226
143
|
}
|
|
227
144
|
|
|
228
|
-
/*await
|
|
145
|
+
/*await this.sql`
|
|
229
146
|
INSERT INTO UserLogin SET
|
|
230
147
|
user = ${user.email},
|
|
231
148
|
date = NOW(),
|
|
@@ -244,7 +161,7 @@ export default abstract class UserAuthBase {
|
|
|
244
161
|
let username = email.split('@')[0];
|
|
245
162
|
|
|
246
163
|
// Prefix username if alreasy existing
|
|
247
|
-
const duplicates = await
|
|
164
|
+
const duplicates = await this.sql`
|
|
248
165
|
FROM User
|
|
249
166
|
WHERE name REGEXP CONCAT('^', ${username}, '[0-9]*$');
|
|
250
167
|
`.count();
|
|
@@ -268,12 +185,12 @@ export default abstract class UserAuthBase {
|
|
|
268
185
|
// Referrer
|
|
269
186
|
if (user.referrer !== undefined) {
|
|
270
187
|
|
|
271
|
-
const refExists = await
|
|
188
|
+
const refExists = await this.sql`FROM User WHERE name = ${user.referrer}`.exists();
|
|
272
189
|
if (!refExists)
|
|
273
190
|
user.referrer = undefined;
|
|
274
191
|
else {
|
|
275
192
|
|
|
276
|
-
await
|
|
193
|
+
await this.sql.upsert('UserStats', {
|
|
277
194
|
user: user.referrer,
|
|
278
195
|
date: new Date,
|
|
279
196
|
refSignups: 1
|
|
@@ -284,8 +201,8 @@ export default abstract class UserAuthBase {
|
|
|
284
201
|
}
|
|
285
202
|
|
|
286
203
|
// Create user
|
|
287
|
-
await
|
|
288
|
-
await
|
|
204
|
+
await this.sql.insert('User', user);
|
|
205
|
+
await this.sql.update('logs.IP', { user_name: username }, { address: request.ip });
|
|
289
206
|
|
|
290
207
|
// Hook
|
|
291
208
|
let redirect: string = '/';
|
|
@@ -296,7 +213,7 @@ export default abstract class UserAuthBase {
|
|
|
296
213
|
// remove user.emailHash
|
|
297
214
|
|
|
298
215
|
// Notif email
|
|
299
|
-
await
|
|
216
|
+
await this.email.send({
|
|
300
217
|
to: app.identity.author.email,
|
|
301
218
|
subject: app.identity.name + ": New User",
|
|
302
219
|
html: JSON.stringify(user)
|
|
@@ -335,7 +252,7 @@ export default abstract class UserAuthBase {
|
|
|
335
252
|
return;
|
|
336
253
|
|
|
337
254
|
// Check if user exists
|
|
338
|
-
const referrerExists = await
|
|
255
|
+
const referrerExists = await this.sql`FROM User WHERE name = ${referrer}`.count();
|
|
339
256
|
if (referrerExists === 0)
|
|
340
257
|
return;
|
|
341
258
|
|
|
@@ -347,7 +264,7 @@ export default abstract class UserAuthBase {
|
|
|
347
264
|
response.cookie('r', referrer);
|
|
348
265
|
|
|
349
266
|
// Count the clic
|
|
350
|
-
await
|
|
267
|
+
await this.sql.upsert('UserStats', {
|
|
351
268
|
user: referrer,
|
|
352
269
|
date: new Date,
|
|
353
270
|
refClics: 1
|
|
@@ -357,5 +274,4 @@ export default abstract class UserAuthBase {
|
|
|
357
274
|
}
|
|
358
275
|
|
|
359
276
|
}
|
|
360
|
-
|
|
361
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