Skip to content

Commit

Permalink
Merge pull request #25 from mconf/develop
Browse files Browse the repository at this point in the history
build: mconf/[email protected]
  • Loading branch information
prlanzarin authored Aug 5, 2024
2 parents ef94c5b + d7c1e7d commit 60ed609
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 87 deletions.
1 change: 0 additions & 1 deletion .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ jobs:
context: .
platforms: linux/amd64
cache-from: type=registry,ref=${{ steps.tag.outputs.IMAGE }}
cache-to: type=registry,ref=${{ steps.tag.outputs.IMAGE }},image-manifest=true,oci-mediatypes=true,mode=max
labels: |
${{ steps.meta.outputs.labels }}
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG-MCONF.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

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

### v2.2.0

* build: merge with bigbluebutton/[email protected] (see CHANGELOG.md)

### v2.1.0

* !build: merge with bigbluebutton/[email protected] (see CHANGELOG.md)
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

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

### v3.2.0

* feat(xapi): add support for Basic auth via meta_secret-lrs-payload
* fix(xapi): LRS credentials wouldn't persist in the database
* fix: remove cache-to from image push to make dockerhub images usable
* build: [email protected]
* build: [email protected]

### v3.1.0

* feat(events): add guest field to user-joined/user-left
Expand Down
69 changes: 15 additions & 54 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "bbb-webhooks",
"version": "3.1.0",
"version": "3.2.0",
"description": "A BigBlueButton mudule for events WebHooks",
"type": "module",
"mconf_version": "2.1.0",
"mconf_version": "2.2.0",
"scripts": {
"start": "node app.js",
"dev-start": "nodemon --watch src --ext js,json,yml,yaml --exec node app.js",
Expand All @@ -21,7 +21,7 @@
"dependencies": {
"bullmq": "4.17.0",
"config": "^3.3.7",
"express": "^4.18.2",
"express": "^4.19.2",
"js-yaml": "^4.1.0",
"luxon": "^3.4.3",
"node-fetch": "^3.3.2",
Expand All @@ -41,7 +41,7 @@
"fast-xml-parser": "^4.3.2",
"jsdoc": "^4.0.2",
"mocha": "^9.2.2",
"nodemon": "^3.0.1",
"nodemon": "^3.1.4",
"pino-pretty": "^10.2.3",
"sinon": "^12.0.1",
"supertest": "^3.4.2"
Expand Down
30 changes: 22 additions & 8 deletions src/out/xapi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,28 @@ If you set `meta_xapi-enabled` to false, no xAPI events will be generated or sen

### meta_secret-lrs-payload
- **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**.
- **Value Format**: Base64-encoded JSON object encrypted with AES 256/PBKDF2 encryption
- **JSON Payload Structure**:
```json
{
"lrs_endpoint": "https://lrs1.example.com",
"lrs_token": "AAF32423SDF5345"
}
```
There are two supported formats for the payload:

- **LRS Token (Bearer authentication)**
- **Value Format**: Base64-encoded JSON object encrypted with AES 256/PBKDF2 encryption
- **JSON Payload Structure**:
```json
{
"lrs_endpoint": "https://lrs1.example.com",
"lrs_token": "AAF32423SDF5345"
}
```
- **LRS Username/Password (Basic authentication)**
- **Value Format**: Base64-encoded JSON object encrypted with AES 256/PBKDF2 encryption
- **JSON Payload Structure**:
```json
{
"lrs_endpoint": "https://lrs1.example.com",
"lrs_username": "user",
"lrs_password": "pass"
}
```

- **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`):
```bash
cat ./lrs.conf | openssl aes-256-cbc -pass "pass:bab3fd92bcd7d464" -pbkdf2 -a -A
Expand Down
6 changes: 3 additions & 3 deletions src/out/xapi/compartment.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export class meetingCompartment extends StorageCompartmentKV {
async addOrUpdateMeetingData(meeting_data) {
const { internal_meeting_id, context_registration, planned_duration,
create_time, meeting_name, xapi_enabled, create_end_actor_name,
lrs_endpoint, lrs_token } = meeting_data;
lrs_payload,
} = meeting_data;

const payload = {
internal_meeting_id,
Expand All @@ -18,8 +19,7 @@ export class meetingCompartment extends StorageCompartmentKV {
meeting_name,
xapi_enabled,
create_end_actor_name,
lrs_endpoint,
lrs_token,
lrs_payload,
};

const mapping = await this.save(payload, {
Expand Down
63 changes: 46 additions & 17 deletions src/out/xapi/xapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,22 @@ export default class XAPI {
}

async postToLRS(statement, meeting_data) {
let { lrs_endpoint, lrs_username, lrs_password } = this.config.lrs;
if (meeting_data.lrs_endpoint !== ''){
lrs_endpoint = meeting_data.lrs_endpoint;
let decrypted_lrs_payload = null;
if (meeting_data.lrs_payload) {
try {
decrypted_lrs_payload = this._decryptLrsPayload(meeting_data.lrs_payload);
} catch (error) {
this.logger.warn("OutXAPI.postToLRS: invalid lrs_payload found on db", {
lrs_payload: meeting_data.lrs_payload,
});
throw new Error('invalid lrs_payload');
}
}
const lrs_token = meeting_data.lrs_token;

const lrs_username = decrypted_lrs_payload?.lrs_username || this.config.lrs?.lrs_username;
const lrs_password = decrypted_lrs_payload?.lrs_password || this.config.lrs?.lrs_password;
const lrs_endpoint = decrypted_lrs_payload?.lrs_endpoint || this.config.lrs?.lrs_endpoint;
const lrs_token = decrypted_lrs_payload?.lrs_token;
const headers = {
Authorization: `Basic ${Buffer.from(
lrs_username + ":" + lrs_password
Expand All @@ -52,9 +63,7 @@ export default class XAPI {
"X-Experience-API-Version": "1.0.0",
};

if (lrs_token !== ''){
headers.Authorization = `Bearer ${lrs_token}`
}
if (lrs_token) headers.Authorization = `Bearer ${lrs_token}`

const requestOptions = {
method: "POST",
Expand All @@ -79,6 +88,18 @@ export default class XAPI {
}
}

_decryptLrsPayload(lrs_payload) {
// Decrypt the lrs_payload with the server secret to check if it is valid
const payload_text = decryptStr(lrs_payload, this.config.server.secret);
const parsedPayload = JSON.parse(payload_text);

if (!parsedPayload.lrs_token && (!parsedPayload.lrs_username || !parsedPayload.lrs_password)) {
throw new Error('invalid lrs_payload, mssing token or username@password');
}

return parsedPayload;
}

async onEvent(event) {
const eventId = event.data.id;

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

const lrs_payload = event.data.attributes.meeting.metadata?.["secret-lrs-payload"];
let lrs_endpoint = '';
let lrs_token = '';

// if lrs_payload exists, decrypts with the server secret and extracts lrs_endpoint and lrs_token from it
if (lrs_payload !== undefined){
const payload_text = decryptStr(lrs_payload, this.config.server.secret);
({lrs_endpoint, lrs_token} = JSON.parse(payload_text));
if (lrs_payload !== undefined) {
try {
// Decrypt the lrs_payload with the server secret to check if it is valid
this._decryptLrsPayload(lrs_payload);
// Store it encrypted
meeting_data.lrs_payload = lrs_payload;
} catch (error) {
this.logger.error("OutXAPI.onEvent: invalid lrs_payload", {
error: error.stack,
lrs_payload
});
return reject(error);
}
}

meeting_data.lrs_endpoint = lrs_endpoint;
meeting_data.lrs_token = lrs_token;

const meeting_create_day = DateTime.fromMillis(
meeting_data.create_time
).toFormat("yyyyMMdd");
Expand Down Expand Up @@ -276,7 +301,11 @@ export default class XAPI {
}
}
if (XAPIStatement !== null && meeting_data.xapi_enabled === 'true') {
await this.postToLRS(XAPIStatement, meeting_data);
try {
await this.postToLRS(XAPIStatement, meeting_data);
} catch (error) {
return reject(error);
}
}
});
}
Expand Down

0 comments on commit 60ed609

Please sign in to comment.