@ejfdelgado/ejflab-back 1.22.2 → 1.24.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ejfdelgado/ejflab-back",
3
- "version": "1.22.2",
3
+ "version": "1.24.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/ejfdelgado/ejflab-back.git"
@@ -140,6 +140,58 @@ export class AuthorizationSrv {
140
140
  });
141
141
  }
142
142
 
143
+ static isUserInGroupInternal(user, groups, and) {
144
+ if (!user) {
145
+ return false;
146
+ }
147
+ const currentGroups = user.groups;
148
+ const notMeet = groups.filter((group) => {
149
+ if (currentGroups.indexOf(group) >= 0) {
150
+ return false;
151
+ }
152
+ return true;
153
+ });
154
+ if (and) {
155
+ //All must have
156
+ return notMeet.length == 0;
157
+ } else {
158
+ // At least one
159
+ return notMeet.length < groups.length;
160
+ }
161
+ }
162
+
163
+ static isUserInAllGroup(groups) {
164
+ return async (req, res, next) => {
165
+ if (res.locals.user) {
166
+ // Hay usuario
167
+ const cumple = this.isUserInGroupInternal(res.locals.user, groups, true);
168
+ if (cumple) {
169
+ next();
170
+ } else {
171
+ res.status(403).send({ message: `User not allowed` });
172
+ }
173
+ } else {
174
+ res.status(403).send({ message: `User not authenticated` });
175
+ }
176
+ }
177
+ }
178
+
179
+ static isUserInSomeGroup(groups) {
180
+ return async (req, res, next) => {
181
+ if (res.locals.user) {
182
+ // Hay usuario
183
+ const cumple = this.isUserInGroupInternal(res.locals.user, groups, false);
184
+ if (cumple) {
185
+ next();
186
+ } else {
187
+ res.status(403).send({ message: `User not allowed` });
188
+ }
189
+ } else {
190
+ res.status(403).send({ message: `User not authenticated` });
191
+ }
192
+ }
193
+ }
194
+
143
195
  static hasPagePermisions(listaOr) {
144
196
  return async (req, res, next) => {
145
197
  if (listaOr.length == 0) {
@@ -35,6 +35,8 @@ import { OpenVideoChatProcessor } from "./callprocessors/OpenVideoChatProcessor.
35
35
  import { CloseVideoChatProcessor } from "./callprocessors/CloseVideoChatProcessor.mjs";
36
36
  import { IncludeOtherPeersProcessor } from "./callprocessors/IncludeOtherPeersProcessor.mjs";
37
37
 
38
+ import stream from "stream";
39
+
38
40
  export class SocketIOCall {
39
41
  static io;
40
42
  static mutex = new Mutex();
@@ -417,7 +419,7 @@ export class SocketIOCall {
417
419
  decoded = decode(buffer);
418
420
  }
419
421
  //console.log(decoded);
420
- const { processorMethod, room, channel } = decoded;
422
+ const { processorMethod, room, channel, data } = decoded;
421
423
  const parts = /(^[^\d\.]+)(\d*)\.(.*)$/.exec(processorMethod);
422
424
  if (!parts) {
423
425
  throw Error(`processorMethod ${processorMethod} does not matches /(^[^\d\.]+)(\d*)\.(.*)$/`);
@@ -490,17 +492,48 @@ export class SocketIOCall {
490
492
  const encoded = encode(decoded);
491
493
  const buffer = Buffer.from(encoded);
492
494
  console.log(`POST to ${postUrl}...`);
493
- const temp = await axios.post(`${postUrl}/syncprocess`, buffer, options);
495
+ let isStream = false;
496
+ if (decoded.data?.streaming) {
497
+ isStream = true;
498
+ options['responseType'] = 'stream';
499
+ console.log("Prepare to receive streaming...");
500
+ }
501
+ const fullUrl = `${postUrl}/syncprocess`;
502
+ console.log(fullUrl);
503
+ const temp = await axios.post(fullUrl, buffer, options);
494
504
  console.log(`POST to ${postUrl}... OK!`);
495
- promesa = Promise.resolve({ data: temp.data });
505
+ //console.log('Response status:', temp.status);
506
+ //console.log('Response headers:', temp.headers);
507
+ promesa = Promise.resolve({ data: temp.data, isStream });
496
508
  }
497
509
 
498
-
499
510
  const respuesta = await promesa;
500
- res.status(200).send({
501
- status: "ok",
502
- response: respuesta
503
- });
511
+ if (!respuesta.isStream) {
512
+ res.status(200).send({
513
+ status: "ok",
514
+ response: respuesta
515
+ });
516
+ } else {
517
+
518
+ res.status(200);
519
+ res.setHeader('Content-Type', 'text/plain; charset=UTF-8');
520
+ res.setHeader('Transfer-Encoding', 'chunked');
521
+
522
+ const pass = new stream.PassThrough();
523
+ pass.on('data', (temp) => {
524
+ res.write(temp);
525
+ res.flush();
526
+ }).on("error", (error) => {
527
+ res.end();
528
+ }).on("finish", () => {
529
+ res.end();
530
+ });
531
+ stream.pipeline(
532
+ respuesta.data,
533
+ pass,
534
+ (err) => { }
535
+ );
536
+ }
504
537
  }
505
538
 
506
539
  static async introspect(req, res, next) {
@@ -5,6 +5,48 @@ import { Usuario } from './Usuario.mjs';
5
5
  import fs from "fs";
6
6
  import { General } from './General.mjs';
7
7
  import { MyConstants } from '@ejfdelgado/ejflab-common/src/MyConstants.js';
8
+ import jwt from 'jsonwebtoken';
9
+ import jwksClient from 'jwks-rsa';
10
+
11
+ const AUTH_PROVIDER = process.env.AUTH_PROVIDER;
12
+ const MICROSOFT_CLIENT_ID = process.env.MICROSOFT_CLIENT_ID;
13
+ const MICROSOFT_TENANT = process.env.MICROSOFT_TENANT;
14
+
15
+ const microsoftClient = jwksClient({
16
+ jwksUri: `https://login.microsoftonline.com/${MICROSOFT_TENANT}/discovery/v2.0/keys`,
17
+ });
18
+
19
+ // Helper to get the signing key
20
+ function getMicrosoftKey(header, callback) {
21
+ microsoftClient.getSigningKey(header.kid, (err, key) => {
22
+ if (err) {
23
+ return callback(err);
24
+ }
25
+ const signingKey = key.getPublicKey();
26
+ callback(null, signingKey);
27
+ });
28
+ }
29
+
30
+ function verifyMicrosoftToken(token) {
31
+ return new Promise((resolve, reject) => {
32
+ jwt.verify(
33
+ token,
34
+ getMicrosoftKey,
35
+ {
36
+ algorithms: ['RS256'], // Tokens are typically signed with RS256
37
+ audience: MICROSOFT_CLIENT_ID, // Replace with your application's client ID
38
+ issuer: `https://login.microsoftonline.com/${MICROSOFT_TENANT}/v2.0`, // Replace with your tenant's issuer
39
+ //issuer: `https://sts.windows.net/${MICROSOFT_TENANT}/`
40
+ },
41
+ (err, decoded) => {
42
+ if (err) {
43
+ return reject(err);
44
+ }
45
+ resolve(decoded);
46
+ }
47
+ );
48
+ });
49
+ }
8
50
 
9
51
  function getFirebaseConfig() {
10
52
  const firebaseJson = fs.readFileSync(MyConstants.FIREBASE_CONFIG_FILE, { encoding: "utf8" });
@@ -54,17 +96,30 @@ async function checkAutenticated(req) {
54
96
  reject(new MyError("Missing Authorization header.", 403));
55
97
  return;
56
98
  }
57
- getAuth()
58
- .verifyIdToken(sessionToken)
59
- .then((decodedToken) => {
60
- resolve({
61
- decodedToken,
62
- sessionToken,
99
+ if (AUTH_PROVIDER == "microsoft") {
100
+ verifyMicrosoftToken(sessionToken)
101
+ .then((decodedToken) => {
102
+ resolve({
103
+ decodedToken,
104
+ sessionToken,
105
+ });
106
+ })
107
+ .catch((error) => {
108
+ reject(new MyError(error.message, 403));
63
109
  });
64
- })
65
- .catch((error) => {
66
- reject(new MyError(error.message, 403));
67
- });
110
+ } else if (AUTH_PROVIDER == "google") {
111
+ getAuth()
112
+ .verifyIdToken(sessionToken)
113
+ .then((decodedToken) => {
114
+ resolve({
115
+ decodedToken,
116
+ sessionToken,
117
+ });
118
+ })
119
+ .catch((error) => {
120
+ reject(new MyError(error.message, 403));
121
+ });
122
+ }
68
123
  });
69
124
  }
70
125
 
@@ -107,7 +162,7 @@ async function checkAuthenticatedSilent(req, res, next) {
107
162
  res.locals.user = new Usuario(res.locals.token);
108
163
  await next();
109
164
  } catch (err) {
110
- //console.log(err);
165
+ console.log(err);
111
166
  res.locals.token = null;
112
167
  res.locals.user = null;
113
168
  await next();
@@ -5,6 +5,8 @@ import { MyStore } from "./MyStore.mjs";
5
5
  import { General } from "./General.mjs";
6
6
  import { MalaPeticionException } from "../MyError.mjs";
7
7
 
8
+ const AUTH_PROVIDER = process.env.AUTH_PROVIDER;
9
+ const groupIdMap = JSON.parse("AUTH_GROUP_ID_MAP" in process.env ? process.env.AUTH_GROUP_ID_MAP : "{}");
8
10
  const USER_TYPE = "user";
9
11
 
10
12
  export class Usuario {
@@ -12,20 +14,39 @@ export class Usuario {
12
14
  id = null;
13
15
  email = null;
14
16
  phone = null;
17
+ groups = [];
15
18
  constructor(token) {
16
19
  this.metadatos = token;
17
20
  if (this.metadatos != null) {
18
21
  if (this.metadatos.email) {
19
22
  this.email = this.metadatos.email;
20
23
  }
21
- const contenedor = this.metadatos["firebase"];
22
- const identidades = contenedor["identities"];
23
- if ("email" in identidades) {
24
- this.id = identidades["email"][0];
25
- this.email = this.id;
26
- } else if ("phone" in identidades) {
27
- this.id = identidades["phone"][0];
28
- this.phone = this.id;
24
+ if (AUTH_PROVIDER == "microsoft") {
25
+ this.id = token.oid;
26
+ this.email = token.preferred_username;
27
+ if (token.groups instanceof Array) {
28
+ this.groups = token.groups.map((idGroup) => {
29
+ if (idGroup in groupIdMap) {
30
+ return groupIdMap[idGroup];
31
+ }
32
+ return idGroup;
33
+ });
34
+ }
35
+ //console.log(`id: ${this.id}`);
36
+ //console.log(`email: ${this.email}`);
37
+ //console.log(`groups: ${JSON.stringify(this.groups)}`);
38
+ } else {
39
+ if ("firebase" in this.metadatos) {
40
+ const contenedor = this.metadatos["firebase"];
41
+ const identidades = contenedor["identities"];
42
+ if ("email" in identidades) {
43
+ this.id = identidades["email"][0];
44
+ this.email = this.id;
45
+ } else if ("phone" in identidades) {
46
+ this.id = identidades["phone"][0];
47
+ this.phone = this.id;
48
+ }
49
+ }
29
50
  }
30
51
  }
31
52
  }