Skip to content

Commit

Permalink
added passwordoption and advanced user interaction
Browse files Browse the repository at this point in the history
  • Loading branch information
svoeth committed Apr 26, 2020
1 parent 4c0ac08 commit a606fe2
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 79 deletions.
9 changes: 8 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,11 @@ PORT=5000
# you can call `sudo bbb-conf --secret` for the following two values
BBB_API_URL=https://demo.bigbluebutton.org/bigbluebutton
BBB_API_SECRET=FIXME
WELCOME_MESSAGE="Welcome to <b>%%CONFNAME%%</b>!<br><br>Invite others with following URL: %%JOINURL%%<br><br>Use a headset to avoid causing background noise for others."

## Welcome Message for new users
WELCOME_MESSAGE="Welcome to<b>%%CONFNAME%%</b>.<br><br>Use a headset to avoid causing background noise for others."
## welcome message for new users if no manual password is set
NOPW_WELCOME_MESSAGE=""Welcome to <b>%%CONFNAME%%</b>!<br><br>Invite others with following URL: %%JOINURL%%<br><br>Use a headset to avoid causing background noise for others."
## welcome message for moderators only. Will only be shown if a manual roompassword is set
MOD_WELCOME_MESSAGE="Dear Moderator,<br><br>Invite others with following URL: %%JOINURL%%<br><br>Provide the following passwords to join:<br>Moderator: %%MPASSWD%%<br>User: %%UPASSWD%%"

26 changes: 22 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
bbb-easy-join
-------------

An easy frontend to BigBlueButton without signups - everybody can just entry a meeting room name, the first user gets moderator level access.
An easy frontend to BigBlueButton without signups - everybody can just enter a meeting room name and an optional password.
The first user gets moderator level access.


<img src="docs/overview.png" width="350" /> <img src="docs/join-room.png" width="350" />

### Setup

Tested with an BBB instance installed with [bbb-install.sh](https://github.com/bigbluebutton/bbb-install).

Installation requires git and bbb to be installed on the same machine

```bash
sudo apt install git
```

Checkout this repository to /var/www/bbb-easy-join

```
git clone https://github.com/stadtulm/bbb-easy-join.git /var/www/bbb-easy-join
git clone https://github.com/svoeth/bbb-easy-join.git /var/www/bbb-easy-join
sudo chown -r bigbluebutton: /var/www/bbb-easy-join
sudo -iu bigbluebutton
cd /var/www/bbb-easy-join
Expand All @@ -23,11 +31,21 @@ Create an `.env` file (use `.env.sample` as a template) and enter your BBB API U

For serving the pages, copy `bbb-easy-join.nginx` to `/etc/bigbluebutton/nginx/`. If there are already `greenlight-redirect.nginx` and/or `greenlight.nginx`, rename (remove the .nginx suffix) or delete them.

If you want to keep the service running, the systemd service file `bbb-easy-join.service` can be copied to `/etc/systemd/system/` and activated by `sudo systemctl daemon-reload`, `sudo systemctl enable bbb-easy-join` and `sudo systemctl start bbb-easy-join`.
You can start the service by running `/usr/bin/node /var/www/bbb-easy-join/app.js`
If you want to keep the service running in background and on startup:
```bash
# copy the systemd service file and register it with systemd
cp bbb-easy-join.service /etc/systemd/system/
sudo systemctl daemon-reload
# activate it to start on startup
sudo systemctl enable bbb-easy-join
# start it immediately
sudo systemctl start bbb-easy-join
```

### Note

Your BigBlueButton Instance is then (if not only reachable from inside your network) public, everybody can create and moderate/present in a meeting. Therefore, you may want to think about disabling recordings, the security of accepting presentation files etc.
Your BigBlueButton Instance is then (if not only reachable from inside your network or protected otherwise) public, everybody can create and moderate/present in a meeting. Therefore, you may want to think about disabling recordings, the security of accepting presentation files etc.

### Credits

Expand Down
119 changes: 106 additions & 13 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const host = process.env.HOST || '0.0.0.0'
const BBB_API_URL = process.env.BBB_API_URL
const BBB_API_SECRET = process.env.BBB_API_SECRET
const WELCOME_MESSAGE = process.env.WELCOME_MESSAGE
const NOPW_WELCOME_MESSAGE = process.env.NOPW_WELCOME_MESSAGE
const MOD_WELCOME_MESSAGE = process.env.MOD_WELCOME_MESSAGE

// TODO: internationalize more
slugify.extend({'ä': 'ae', 'ü': 'ue', 'ö': 'oe', 'ß': 'ss'})
Expand Down Expand Up @@ -56,6 +58,15 @@ const joinMeetingUrl = function (id, name, password) {
return buildApiUrl('join', params);
}

const generateRandomPassword = function () {
const alpha = 'qwertyuiopasdfghjklzxcvbnm';
var password = '';
for (let i=0;i<24;i++) {
password += alpha[Math.floor(Math.random() * alpha.length)]
}
return password;
}

app.use(express.static('public'))
app.set('view engine', 'ejs')
app.set('trust proxy', 'loopback')
Expand All @@ -66,39 +77,105 @@ app.get('/', function(req, res) {
})

app.get('/b', function (req, res) {
res.render('index')
res.render('index', {
roomName: req.query.roomName,
roomBlocked: req.query.roomBlocked,
roomMissing: req.query.roomMissing })
})


// create a room (optionally with password)
// if password is set provide mod and user pw for welcome message
app.post('/b', async function (req, res) {
var roomName = req.body.room
var room = slugify(roomName, { lower: true })
var options = {}

// if user provides password within room creation, set prefix "user-" otherwise set it to "auto-"
var room_password;
var custom_password = req.body.room_password;
if (custom_password) {
// generate random moderator password to give possibility to provide it in welcome message
var moderator_password = generateRandomPassword();
room_password = "user-" + custom_password;
// add passwords to options for room creation
var options = { 'attendeePW' : room_password, 'moderatorPW' : moderator_password }
}else {
// create random password with "auto-" prefix. Not entirely necessary, but theoretically random password
// could start with "user-" and might therefore accidentally be interpreted as manual pw
room_password = "auto-" + generateRandomPassword();
var options = { 'attendeePW' : room_password }
}

//set welcome message(s)
if (typeof WELCOME_MESSAGE === 'string') {
var url = req.protocol + '://' + req.get('host') + '/b/' + room
var url = req.protocol + '://' + req.get('host') + '/b/' + roomName
var joinpattern = new RegExp('%%JOINURL%%', 'g')
options.welcome = WELCOME_MESSAGE.replace(joinpattern, url)
//if the room has a manual password set we will provide two welcome msgs and only inform moderators about joining options and passwords
if (room_password.startsWith('user-')) {
var mpasswd = new RegExp('%%MPASSWD%%', 'g')
var upasswd = new RegExp('%%UPASSWD%%', 'g')
var modwelcome = MOD_WELCOME_MESSAGE.replace(joinpattern, url)
modwelcome = modwelcome.replace(mpasswd, moderator_password)
options.moderatorOnlyMessage = modwelcome.replace(upasswd, custom_password)
options.welcome = WELCOME_MESSAGE
} else {
// welcome message with no manual pw gets joinurl
options.welcome = NOPW_WELCOME_MESSAGE.replace(joinpattern, url)
}
}

var meet = await createMeeting(roomName, room, options)
if (meet.returncode === 'FAILED' && meet.messageKey !== 'idNotUnique') {
res.redirect('/b')
return
//check if roomcreation was not succesful
if (meet.returncode === 'FAILED') {
//if room exists put warning message on page - else just go back
if (meet.messageKey === 'idNotUnique') {
// timeout as a very basic bruteforce prevention to prevent room search
setTimeout(function (){
res.redirect('/b' + `?roomName=${encodeURIComponent(roomName)}&roomBlocked=true`);
}, 1000);
return
} else {
res.redirect('/b');
return
}
}

res.redirect('/b/' + room)
//slugify roomName because room contains prefix
res.redirect('/b/' + room )
})

app.get('/b/:room', async function (req, res){
var room = slugify(req.params.room)

// added slugify lower to prevent error if accidental uppercase or special characters are in adress
var room = slugify(req.params.room, { lower: true })
var info = await getMeetingInfo(room)

//check if meeting info was not succesfully grabbed and in case redirect with error message (room not created)
if (info.returncode === 'FAILED') {
res.redirect('/b')
// timeout as a very basic bruteforce prevention to prevent room search
setTimeout(function (){
res.redirect('/b' + `?roomName=${encodeURIComponent(req.params.room)}&roomMissing=true`)
}, 1000);
return
}

res.render('join', { room: room, info: info })
// This variable decides whether we display a password dialogue.
// Normally, meetings require no user provided password...
var requiresPassword = false;
// ... unless one is set
if (info.attendeePW.startsWith('user-')) {
requiresPassword = true;
}
// ... except when the room is empty, then we'll become the moderator anyway.
if (info.hasUserJoined == false || info.participantCount == 0) {
requiresPassword = false;
}

res.render('join', {
room: room,
info: info,
requiresPassword: requiresPassword,
username: req.query.username,
wrongPw: req.query.wrongPw })
})

app.post('/b/:room', async function (req, res) {
Expand All @@ -111,10 +188,26 @@ app.post('/b/:room', async function (req, res) {
}

var password = info.attendeePW
// check if there is anybody in the meeting - if not give moderator rights
if (info.hasUserJoined == false || info.participantCount == 0) {
password = info.moderatorPW
// if there is somebody in the meeting get the attendee pw and check if it is manually set (starts wit user-) otherwise just join as attende
} else {
password = info.attendeePW;
if (password.startsWith('user-')) {
// check if provided login password matches required password if not redirect to login page with error
if (password != "user-" + req.body.room_password) {
password = info.moderatorPW;
if (password != req.body.room_password) {
// timeout as a very basic bruteforce prevention - double time for password try (2 seconds)
setTimeout(function (){
res.redirect('/b/' + info.meetingName + `?username=${encodeURIComponent(name)}&wrongPw=true`);
}, 2000);
return;
}
}
}
}

res.redirect(joinMeetingUrl(room, name, password))
})

Expand Down
Binary file modified docs/overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit a606fe2

Please sign in to comment.