Skip to content

Commit 60ed609

Browse files
authored
Merge pull request #25 from mconf/develop
build: mconf/bbb-webhooks@v2.2.0
2 parents ef94c5b + d7c1e7d commit 60ed609

File tree

8 files changed

+102
-87
lines changed

8 files changed

+102
-87
lines changed

.github/workflows/docker-image.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ jobs:
6969
context: .
7070
platforms: linux/amd64
7171
cache-from: type=registry,ref=${{ steps.tag.outputs.IMAGE }}
72-
cache-to: type=registry,ref=${{ steps.tag.outputs.IMAGE }},image-manifest=true,oci-mediatypes=true,mode=max
7372
labels: |
7473
${{ steps.meta.outputs.labels }}
7574

CHANGELOG-MCONF.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
All notables changes *unique to Mconf's fork of bbb-webhooks* are documented in this file.
44

5+
### v2.2.0
6+
7+
* build: merge with bigbluebutton/webhooks@v3.2.0 (see CHANGELOG.md)
8+
59
### v2.1.0
610

711
* !build: merge with bigbluebutton/webhooks@v3.1.0 (see CHANGELOG.md)

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
### v3.2.0
6+
7+
* feat(xapi): add support for Basic auth via meta_secret-lrs-payload
8+
* fix(xapi): LRS credentials wouldn't persist in the database
9+
* fix: remove cache-to from image push to make dockerhub images usable
10+
* build: nodemon@3.1.4
11+
* build: express@4.19.2
12+
513
### v3.1.0
614

715
* feat(events): add guest field to user-joined/user-left

package-lock.json

Lines changed: 15 additions & 54 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"name": "bbb-webhooks",
3-
"version": "3.1.0",
3+
"version": "3.2.0",
44
"description": "A BigBlueButton mudule for events WebHooks",
55
"type": "module",
6-
"mconf_version": "2.1.0",
6+
"mconf_version": "2.2.0",
77
"scripts": {
88
"start": "node app.js",
99
"dev-start": "nodemon --watch src --ext js,json,yml,yaml --exec node app.js",
@@ -21,7 +21,7 @@
2121
"dependencies": {
2222
"bullmq": "4.17.0",
2323
"config": "^3.3.7",
24-
"express": "^4.18.2",
24+
"express": "^4.19.2",
2525
"js-yaml": "^4.1.0",
2626
"luxon": "^3.4.3",
2727
"node-fetch": "^3.3.2",
@@ -41,7 +41,7 @@
4141
"fast-xml-parser": "^4.3.2",
4242
"jsdoc": "^4.0.2",
4343
"mocha": "^9.2.2",
44-
"nodemon": "^3.0.1",
44+
"nodemon": "^3.1.4",
4545
"pino-pretty": "^10.2.3",
4646
"sinon": "^12.0.1",
4747
"supertest": "^3.4.2"

src/out/xapi/README.md

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,28 @@ If you set `meta_xapi-enabled` to false, no xAPI events will be generated or sen
6464

6565
### meta_secret-lrs-payload
6666
- **Description**: This parameter allows you to specify the credentials and endpoint of the Learning Record Store (LRS) where the xAPI events will be sent. The payload is a Base64-encoded string representing a JSON object encrypted (AES 256/PBKDF2) using the **server secret** as the **passphrase**.
67-
- **Value Format**: Base64-encoded JSON object encrypted with AES 256/PBKDF2 encryption
68-
- **JSON Payload Structure**:
69-
```json
70-
{
71-
"lrs_endpoint": "https://lrs1.example.com",
72-
"lrs_token": "AAF32423SDF5345"
73-
}
74-
```
67+
There are two supported formats for the payload:
68+
69+
- **LRS Token (Bearer authentication)**
70+
- **Value Format**: Base64-encoded JSON object encrypted with AES 256/PBKDF2 encryption
71+
- **JSON Payload Structure**:
72+
```json
73+
{
74+
"lrs_endpoint": "https://lrs1.example.com",
75+
"lrs_token": "AAF32423SDF5345"
76+
}
77+
```
78+
- **LRS Username/Password (Basic authentication)**
79+
- **Value Format**: Base64-encoded JSON object encrypted with AES 256/PBKDF2 encryption
80+
- **JSON Payload Structure**:
81+
```json
82+
{
83+
"lrs_endpoint": "https://lrs1.example.com",
84+
"lrs_username": "user",
85+
"lrs_password": "pass"
86+
}
87+
```
88+
7589
- **Encrypting the Payload**: The Payload should be encrypted with the server secret using the following bash command (provided the lrs credential are in the `lrs.conf` file and server secret is `bab3fd92bcd7d464`):
7690
```bash
7791
cat ./lrs.conf | openssl aes-256-cbc -pass "pass:bab3fd92bcd7d464" -pbkdf2 -a -A

src/out/xapi/compartment.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ export class meetingCompartment extends StorageCompartmentKV {
88
async addOrUpdateMeetingData(meeting_data) {
99
const { internal_meeting_id, context_registration, planned_duration,
1010
create_time, meeting_name, xapi_enabled, create_end_actor_name,
11-
lrs_endpoint, lrs_token } = meeting_data;
11+
lrs_payload,
12+
} = meeting_data;
1213

1314
const payload = {
1415
internal_meeting_id,
@@ -18,8 +19,7 @@ export class meetingCompartment extends StorageCompartmentKV {
1819
meeting_name,
1920
xapi_enabled,
2021
create_end_actor_name,
21-
lrs_endpoint,
22-
lrs_token,
22+
lrs_payload,
2323
};
2424

2525
const mapping = await this.save(payload, {

src/out/xapi/xapi.js

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,22 @@ export default class XAPI {
3939
}
4040

4141
async postToLRS(statement, meeting_data) {
42-
let { lrs_endpoint, lrs_username, lrs_password } = this.config.lrs;
43-
if (meeting_data.lrs_endpoint !== ''){
44-
lrs_endpoint = meeting_data.lrs_endpoint;
42+
let decrypted_lrs_payload = null;
43+
if (meeting_data.lrs_payload) {
44+
try {
45+
decrypted_lrs_payload = this._decryptLrsPayload(meeting_data.lrs_payload);
46+
} catch (error) {
47+
this.logger.warn("OutXAPI.postToLRS: invalid lrs_payload found on db", {
48+
lrs_payload: meeting_data.lrs_payload,
49+
});
50+
throw new Error('invalid lrs_payload');
51+
}
4552
}
46-
const lrs_token = meeting_data.lrs_token;
53+
54+
const lrs_username = decrypted_lrs_payload?.lrs_username || this.config.lrs?.lrs_username;
55+
const lrs_password = decrypted_lrs_payload?.lrs_password || this.config.lrs?.lrs_password;
56+
const lrs_endpoint = decrypted_lrs_payload?.lrs_endpoint || this.config.lrs?.lrs_endpoint;
57+
const lrs_token = decrypted_lrs_payload?.lrs_token;
4758
const headers = {
4859
Authorization: `Basic ${Buffer.from(
4960
lrs_username + ":" + lrs_password
@@ -52,9 +63,7 @@ export default class XAPI {
5263
"X-Experience-API-Version": "1.0.0",
5364
};
5465

55-
if (lrs_token !== ''){
56-
headers.Authorization = `Bearer ${lrs_token}`
57-
}
66+
if (lrs_token) headers.Authorization = `Bearer ${lrs_token}`
5867

5968
const requestOptions = {
6069
method: "POST",
@@ -79,6 +88,18 @@ export default class XAPI {
7988
}
8089
}
8190

91+
_decryptLrsPayload(lrs_payload) {
92+
// Decrypt the lrs_payload with the server secret to check if it is valid
93+
const payload_text = decryptStr(lrs_payload, this.config.server.secret);
94+
const parsedPayload = JSON.parse(payload_text);
95+
96+
if (!parsedPayload.lrs_token && (!parsedPayload.lrs_username || !parsedPayload.lrs_password)) {
97+
throw new Error('invalid lrs_payload, mssing token or username@password');
98+
}
99+
100+
return parsedPayload;
101+
}
102+
82103
async onEvent(event) {
83104
const eventId = event.data.id;
84105

@@ -105,18 +126,22 @@ export default class XAPI {
105126
meeting_data.create_end_actor_name = event.data.attributes.meeting.metadata?.["xapi-create-end-actor-name"] || "<unknown>";
106127

107128
const lrs_payload = event.data.attributes.meeting.metadata?.["secret-lrs-payload"];
108-
let lrs_endpoint = '';
109-
let lrs_token = '';
110-
111129
// if lrs_payload exists, decrypts with the server secret and extracts lrs_endpoint and lrs_token from it
112-
if (lrs_payload !== undefined){
113-
const payload_text = decryptStr(lrs_payload, this.config.server.secret);
114-
({lrs_endpoint, lrs_token} = JSON.parse(payload_text));
130+
if (lrs_payload !== undefined) {
131+
try {
132+
// Decrypt the lrs_payload with the server secret to check if it is valid
133+
this._decryptLrsPayload(lrs_payload);
134+
// Store it encrypted
135+
meeting_data.lrs_payload = lrs_payload;
136+
} catch (error) {
137+
this.logger.error("OutXAPI.onEvent: invalid lrs_payload", {
138+
error: error.stack,
139+
lrs_payload
140+
});
141+
return reject(error);
142+
}
115143
}
116144

117-
meeting_data.lrs_endpoint = lrs_endpoint;
118-
meeting_data.lrs_token = lrs_token;
119-
120145
const meeting_create_day = DateTime.fromMillis(
121146
meeting_data.create_time
122147
).toFormat("yyyyMMdd");
@@ -276,7 +301,11 @@ export default class XAPI {
276301
}
277302
}
278303
if (XAPIStatement !== null && meeting_data.xapi_enabled === 'true') {
279-
await this.postToLRS(XAPIStatement, meeting_data);
304+
try {
305+
await this.postToLRS(XAPIStatement, meeting_data);
306+
} catch (error) {
307+
return reject(error);
308+
}
280309
}
281310
});
282311
}

0 commit comments

Comments
 (0)