2017-04-25 04:18:07 +08:00
|
|
|
|
2017-03-11 02:33:46 +08:00
|
|
|
import { Tracker } from 'meteor/tracker';
|
|
|
|
|
2016-07-07 20:50:32 +08:00
|
|
|
import Storage from '/imports/ui/services/storage/session';
|
2017-03-11 02:33:46 +08:00
|
|
|
|
2017-06-19 19:57:32 +08:00
|
|
|
import Users from '/imports/api/1.1/users';
|
2017-05-12 20:24:06 +08:00
|
|
|
import { makeCall, logClient } from '/imports/ui/services/api';
|
2016-06-02 21:46:35 +08:00
|
|
|
|
2017-05-05 22:30:15 +08:00
|
|
|
const CONNECTION_TIMEOUT = Meteor.settings.public.app.connectionTimeout;
|
|
|
|
|
2016-07-11 21:45:24 +08:00
|
|
|
class Auth {
|
|
|
|
constructor() {
|
|
|
|
this._meetingID = Storage.getItem('meetingID');
|
|
|
|
this._userID = Storage.getItem('userID');
|
|
|
|
this._authToken = Storage.getItem('authToken');
|
2017-03-11 02:33:46 +08:00
|
|
|
this._loggedIn = {
|
|
|
|
value: false,
|
2017-06-03 03:25:02 +08:00
|
|
|
tracker: new Tracker.Dependency(),
|
2017-03-11 02:33:46 +08:00
|
|
|
};
|
2016-07-11 21:45:24 +08:00
|
|
|
}
|
2016-06-02 21:46:35 +08:00
|
|
|
|
2016-07-11 21:45:24 +08:00
|
|
|
get meetingID() {
|
|
|
|
return this._meetingID;
|
|
|
|
}
|
2016-06-02 21:46:35 +08:00
|
|
|
|
2016-07-11 21:45:24 +08:00
|
|
|
set meetingID(meetingID) {
|
|
|
|
this._meetingID = meetingID;
|
|
|
|
Storage.setItem('meetingID', this._meetingID);
|
|
|
|
}
|
2016-06-02 21:46:35 +08:00
|
|
|
|
2016-07-11 21:45:24 +08:00
|
|
|
get userID() {
|
|
|
|
return this._userID;
|
|
|
|
}
|
2016-06-02 21:46:35 +08:00
|
|
|
|
2016-07-11 21:45:24 +08:00
|
|
|
set userID(userID) {
|
|
|
|
this._userID = userID;
|
|
|
|
Storage.setItem('userID', this._userID);
|
|
|
|
}
|
2016-06-18 06:15:11 +08:00
|
|
|
|
2016-07-11 21:45:24 +08:00
|
|
|
get token() {
|
|
|
|
return this._authToken;
|
2016-06-25 07:09:32 +08:00
|
|
|
}
|
2016-06-18 06:15:11 +08:00
|
|
|
|
2016-07-11 21:45:24 +08:00
|
|
|
set token(authToken) {
|
|
|
|
this._authToken = authToken;
|
|
|
|
Storage.setItem('authToken', this._authToken);
|
|
|
|
}
|
|
|
|
|
2017-03-10 03:50:21 +08:00
|
|
|
get loggedIn() {
|
2017-03-11 02:33:46 +08:00
|
|
|
this._loggedIn.tracker.depend();
|
|
|
|
return this._loggedIn.value;
|
2017-03-10 03:50:21 +08:00
|
|
|
}
|
|
|
|
|
2017-03-11 02:33:46 +08:00
|
|
|
set loggedIn(value) {
|
|
|
|
this._loggedIn.value = value;
|
|
|
|
this._loggedIn.tracker.changed();
|
|
|
|
}
|
|
|
|
|
2017-03-17 22:23:00 +08:00
|
|
|
get credentials() {
|
|
|
|
return {
|
|
|
|
meetingId: this.meetingID,
|
|
|
|
requesterUserId: this.userID,
|
|
|
|
requesterToken: this.token,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-04-27 03:56:29 +08:00
|
|
|
set(meetingId, requesterUserId, requesterToken) {
|
|
|
|
this.meetingID = meetingId;
|
|
|
|
this.userID = requesterUserId;
|
|
|
|
this.token = requesterToken;
|
|
|
|
}
|
|
|
|
|
2017-03-17 22:23:00 +08:00
|
|
|
set credentials(value) {
|
|
|
|
throw 'Credentials are read-only';
|
|
|
|
}
|
|
|
|
|
|
|
|
clearCredentials() {
|
|
|
|
this.meetingID = null;
|
|
|
|
this.userID = null;
|
|
|
|
this.token = null;
|
|
|
|
this.loggedIn = false;
|
|
|
|
|
|
|
|
return Promise.resolve(...arguments);
|
2017-06-03 03:25:02 +08:00
|
|
|
}
|
2017-03-17 22:23:00 +08:00
|
|
|
|
|
|
|
logout() {
|
|
|
|
if (!this.loggedIn) {
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
2017-05-12 03:18:34 +08:00
|
|
|
const credentialsSnapshot = {
|
|
|
|
meetingId: this.meetingID,
|
|
|
|
requesterUserId: this.userID,
|
|
|
|
requesterToken: this.token,
|
|
|
|
};
|
|
|
|
|
|
|
|
// make sure users who did not connect are not added to the meeting
|
|
|
|
// do **not** use the custom call - it relies on expired data
|
|
|
|
Meteor.call('userLogout', credentialsSnapshot, (error, result) => {
|
|
|
|
if (error) {
|
2017-05-12 20:24:06 +08:00
|
|
|
logClient('error', { error, method: 'userLogout', credentialsSnapshot });
|
2017-05-12 03:18:34 +08:00
|
|
|
} else {
|
|
|
|
this.fetchLogoutUrl()
|
2017-03-17 22:23:00 +08:00
|
|
|
.then(this.clearCredentials)
|
|
|
|
.then(resolve);
|
2017-05-12 03:18:34 +08:00
|
|
|
}
|
2017-03-17 22:23:00 +08:00
|
|
|
});
|
|
|
|
});
|
2017-06-03 03:25:02 +08:00
|
|
|
}
|
2017-03-17 22:23:00 +08:00
|
|
|
|
2017-04-27 03:56:29 +08:00
|
|
|
authenticate(force) {
|
|
|
|
if (this.loggedIn && !force) return Promise.resolve();
|
2017-04-18 01:14:31 +08:00
|
|
|
|
2017-03-11 02:33:46 +08:00
|
|
|
return this._subscribeToCurrentUser()
|
|
|
|
.then(this._addObserverToValidatedField.bind(this));
|
|
|
|
}
|
|
|
|
|
|
|
|
_subscribeToCurrentUser() {
|
2017-03-17 22:23:00 +08:00
|
|
|
const credentials = this.credentials;
|
2017-03-11 02:33:46 +08:00
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
Tracker.autorun((c) => {
|
2017-05-12 03:18:34 +08:00
|
|
|
if (!(credentials.meetingId && credentials.requesterToken && credentials.requesterUserId)) {
|
2017-05-17 23:24:12 +08:00
|
|
|
return reject({
|
2017-05-12 03:18:34 +08:00
|
|
|
error: 500,
|
|
|
|
description: 'Authentication subscription failed due to missing credentials.',
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-03-11 02:33:46 +08:00
|
|
|
setTimeout(() => {
|
|
|
|
c.stop();
|
2017-04-18 01:14:31 +08:00
|
|
|
reject({
|
|
|
|
error: 500,
|
|
|
|
description: 'Authentication subscription timeout.',
|
|
|
|
});
|
|
|
|
}, 5000);
|
2017-03-11 02:33:46 +08:00
|
|
|
|
|
|
|
const subscription = Meteor.subscribe('current-user', credentials);
|
|
|
|
if (!subscription.ready()) return;
|
|
|
|
|
|
|
|
resolve(c);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
_addObserverToValidatedField(prevComp) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const validationTimeout = setTimeout(() => {
|
2017-04-18 01:14:31 +08:00
|
|
|
clearTimeout(validationTimeout);
|
|
|
|
prevComp.stop();
|
2017-03-11 02:33:46 +08:00
|
|
|
this.clearCredentials();
|
2017-04-18 01:14:31 +08:00
|
|
|
reject({
|
|
|
|
error: 500,
|
|
|
|
description: 'Authentication timeout.',
|
|
|
|
});
|
2017-05-05 22:30:15 +08:00
|
|
|
}, CONNECTION_TIMEOUT);
|
2017-03-11 02:33:46 +08:00
|
|
|
|
|
|
|
const didValidate = () => {
|
|
|
|
this.loggedIn = true;
|
|
|
|
clearTimeout(validationTimeout);
|
|
|
|
prevComp.stop();
|
|
|
|
resolve();
|
|
|
|
};
|
|
|
|
|
|
|
|
Tracker.autorun((c) => {
|
|
|
|
const selector = { meetingId: this.meetingID, userId: this.userID };
|
|
|
|
const query = Users.find(selector);
|
|
|
|
|
|
|
|
const handle = query.observeChanges({
|
|
|
|
changed: (id, fields) => {
|
|
|
|
if (fields.validated === true) {
|
|
|
|
c.stop();
|
|
|
|
didValidate();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fields.validated === false) {
|
|
|
|
c.stop();
|
|
|
|
this.clearCredentials();
|
2017-04-18 01:14:31 +08:00
|
|
|
reject({
|
|
|
|
error: 401,
|
|
|
|
description: 'Authentication failed.',
|
|
|
|
});
|
2017-03-11 02:33:46 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2017-06-02 04:40:55 +08:00
|
|
|
makeCall('validateAuthToken');
|
2017-03-11 02:33:46 +08:00
|
|
|
});
|
2016-07-11 21:45:24 +08:00
|
|
|
}
|
|
|
|
|
2017-03-17 22:23:00 +08:00
|
|
|
fetchLogoutUrl() {
|
2017-06-03 03:25:02 +08:00
|
|
|
const url = '/bigbluebutton/api/enter';
|
2016-06-18 06:15:11 +08:00
|
|
|
|
2017-03-17 22:23:00 +08:00
|
|
|
return fetch(url)
|
|
|
|
.then(response => response.json())
|
|
|
|
.then(data => Promise.resolve(data.response.logoutURL));
|
2016-07-11 21:45:24 +08:00
|
|
|
}
|
2017-06-03 03:25:02 +08:00
|
|
|
}
|
2016-06-29 02:50:44 +08:00
|
|
|
|
2017-06-03 03:25:02 +08:00
|
|
|
const AuthSingleton = new Auth();
|
2017-05-16 23:37:17 +08:00
|
|
|
export default AuthSingleton;
|