diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/MeetingDAO.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/MeetingDAO.scala index b6ea0d16e9..ed8e9362da 100644 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/MeetingDAO.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/MeetingDAO.scala @@ -19,6 +19,7 @@ case class MeetingDbModel( presentationUploadExternalDescription: String, presentationUploadExternalUrl: String, learningDashboardAccessToken: String, + logoutUrl: String, createdTime: Long, durationInSeconds: Int ) @@ -36,6 +37,7 @@ class MeetingDbTableDef(tag: Tag) extends Table[MeetingDbModel](tag, None, "meet presentationUploadExternalDescription, presentationUploadExternalUrl, learningDashboardAccessToken, + logoutUrl, createdTime, durationInSeconds ) <> (MeetingDbModel.tupled, MeetingDbModel.unapply) @@ -50,6 +52,7 @@ class MeetingDbTableDef(tag: Tag) extends Table[MeetingDbModel](tag, None, "meet val presentationUploadExternalDescription = column[String]("presentationUploadExternalDescription") val presentationUploadExternalUrl = column[String]("presentationUploadExternalUrl") val learningDashboardAccessToken = column[String]("learningDashboardAccessToken") + val logoutUrl = column[String]("logoutUrl") val createdTime = column[Long]("createdTime") val durationInSeconds = column[Int]("durationInSeconds") } @@ -70,6 +73,7 @@ object MeetingDAO { presentationUploadExternalDescription = meetingProps.meetingProp.presentationUploadExternalDescription, presentationUploadExternalUrl = meetingProps.meetingProp.presentationUploadExternalUrl, learningDashboardAccessToken = meetingProps.password.learningDashboardAccessToken, + logoutUrl = meetingProps.systemProps.logoutUrl, createdTime = meetingProps.durationProps.createdTime, durationInSeconds = meetingProps.durationProps.duration * 60 ) diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/domain/Meeting2x.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/domain/Meeting2x.scala index 4a52394a70..c729d6be73 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/domain/Meeting2x.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/domain/Meeting2x.scala @@ -68,7 +68,8 @@ case class LockSettingsProps( ) case class SystemProps( - html5InstanceId: Int + html5InstanceId: Int, + logoutUrl: String, ) case class GroupProps( diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java index 54d20bffea..b865a9f3d8 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/MeetingService.java @@ -411,7 +411,7 @@ public class MeetingService implements MessageListener { m.getUserInactivityInspectTimerInMinutes(), m.getUserInactivityThresholdInMinutes(), m.getUserActivitySignResponseDelayInMinutes(), m.getEndWhenNoModerator(), m.getEndWhenNoModeratorDelayInMinutes(), m.getMuteOnStart(), m.getAllowModsToUnmuteUsers(), m.getAllowModsToEjectCameras(), m.getMeetingKeepEvents(), - m.breakoutRoomsParams, m.lockSettingsParams, m.getHtml5InstanceId(), + m.breakoutRoomsParams, m.lockSettingsParams, m.getHtml5InstanceId(), m.getLogoutUrl(), m.getGroups(), m.getDisabledFeatures(), m.getNotifyRecordingIsOn(), m.getPresentationUploadExternalDescription(), m.getPresentationUploadExternalUrl(), m.getOverrideClientSettings()); diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api2/IBbbWebApiGWApp.java b/bbb-common-web/src/main/java/org/bigbluebutton/api2/IBbbWebApiGWApp.java index 0ed811b3cc..a9462f501e 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api2/IBbbWebApiGWApp.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api2/IBbbWebApiGWApp.java @@ -42,6 +42,7 @@ public interface IBbbWebApiGWApp { BreakoutRoomsParams breakoutParams, LockSettingsParams lockSettingsParams, Integer html5InstanceId, + String logoutUrl, ArrayList groups, ArrayList disabledFeatures, Boolean notifyRecordingIsOn, diff --git a/bbb-common-web/src/main/scala/org/bigbluebutton/api2/BbbWebApiGWApp.scala b/bbb-common-web/src/main/scala/org/bigbluebutton/api2/BbbWebApiGWApp.scala index 24995506ee..eafd968e2f 100755 --- a/bbb-common-web/src/main/scala/org/bigbluebutton/api2/BbbWebApiGWApp.scala +++ b/bbb-common-web/src/main/scala/org/bigbluebutton/api2/BbbWebApiGWApp.scala @@ -149,6 +149,7 @@ class BbbWebApiGWApp( breakoutParams: BreakoutRoomsParams, lockSettingsParams: LockSettingsParams, html5InstanceId: java.lang.Integer, + logoutUrl: String, groups: java.util.ArrayList[Group], disabledFeatures: java.util.ArrayList[String], notifyRecordingIsOn: java.lang.Boolean, @@ -230,7 +231,8 @@ class BbbWebApiGWApp( ) val systemProps = SystemProps( - html5InstanceId + html5InstanceId, + logoutUrl ) val groupsAsVector: Vector[GroupProps] = groups.asScala.toVector.map(g => GroupProps(g.getGroupId(), g.getName(), g.getUsersExtId().asScala.toVector)) diff --git a/bbb-graphql-middleware/graphql.nginx b/bbb-graphql-middleware/graphql.nginx index e312591986..8ebe1629b2 100644 --- a/bbb-graphql-middleware/graphql.nginx +++ b/bbb-graphql-middleware/graphql.nginx @@ -14,3 +14,11 @@ location /v1/graphql { #proxy_pass http://127.0.0.1:8080; #Hasura proxy_pass http://127.0.0.1:8378; #Graphql Middleware } + +location /api/rest { + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + proxy_pass http://127.0.0.1:8080; #Hasura +} diff --git a/bbb-graphql-server/bbb_schema.sql b/bbb-graphql-server/bbb_schema.sql index 2afcfc0475..86d2b0b5f8 100644 --- a/bbb-graphql-server/bbb_schema.sql +++ b/bbb-graphql-server/bbb_schema.sql @@ -20,6 +20,7 @@ create table "meeting" ( "presentationUploadExternalUrl" varchar(500), "learningDashboardAccessToken" varchar(100), "html5InstanceId" varchar(100), + "logoutUrl" varchar(500), "createdTime" bigint, "durationInSeconds" integer ); @@ -398,7 +399,7 @@ AS SELECT "user"."userId", "user"."raiseHand", "user"."emoji", "user"."guest", --- "user"."guestStatus", + "user"."guestStatus", "user"."mobile", "user"."clientType", "user"."enforceLayout", @@ -437,10 +438,10 @@ rank() OVER ( ) as "positionInWaitingQueue", u."isAllowed", u."isDenied", -COALESCE(u."guestLobbyMessage",mup."guestLobbyMessage") AS "guestLobbyMessage" +COALESCE(NULLIF(u."guestLobbyMessage",''),NULLIF(mup."guestLobbyMessage",'')) AS "guestLobbyMessage" FROM "user" u JOIN "meeting_usersPolicies" mup using("meetingId") -where u."guestStatus" != 'ALLOW'; +where u."guestStatus" = 'WAIT'; --v_user_ref will be used only as foreign key (not possible to fetch this table directly through graphql) --it is necessary because v_user has some conditions like "lockSettings-hideUserList" diff --git a/bbb-graphql-server/graphql.nginx b/bbb-graphql-server/graphql.nginx index 959a1f4552..7787d2b973 100644 --- a/bbb-graphql-server/graphql.nginx +++ b/bbb-graphql-server/graphql.nginx @@ -13,3 +13,11 @@ location /v1/graphql { proxy_set_header Host $host; proxy_pass http://127.0.0.1:8080; #Hasura } + +location /api/rest { + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + proxy_pass http://127.0.0.1:8080; #Hasura +} diff --git a/bbb-graphql-server/metadata/allow_list.yaml b/bbb-graphql-server/metadata/allow_list.yaml index fe51488c70..935c079495 100644 --- a/bbb-graphql-server/metadata/allow_list.yaml +++ b/bbb-graphql-server/metadata/allow_list.yaml @@ -1 +1,3 @@ -[] +- collection: allowed-queries + scope: + global: true diff --git a/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_meeting.yaml b/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_meeting.yaml index ad0c5ed662..e4001a6829 100644 --- a/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_meeting.yaml +++ b/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_meeting.yaml @@ -154,3 +154,13 @@ select_permissions: filter: meetingId: _eq: X-Hasura-MeetingId + - role: pre_join_bbb_client + permission: + columns: + - logoutUrl + - meetingId + - name + filter: + meetingId: + _eq: X-Hasura-MeetingId + comment: "" diff --git a/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_user_current.yaml b/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_user_current.yaml index 91e9594522..90f43241b5 100644 --- a/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_user_current.yaml +++ b/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_user_current.yaml @@ -25,7 +25,7 @@ object_relationships: remote_table: name: v_user_customParameter schema: public - - name: guestStatus + - name: guestStatusDetails using: manual_configuration: column_mapping: @@ -146,6 +146,7 @@ select_permissions: - expired - extId - guest + - guestStatus - hasDrawPermissionOnCurrentPage - isDialIn - isModerator @@ -182,6 +183,7 @@ select_permissions: - expired - extId - guest + - guestStatus - joinErrorCode - joinErrorMessage - joined diff --git a/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_user_guest.yaml b/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_user_guest.yaml index 234361ccee..3aed1906ef 100644 --- a/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_user_guest.yaml +++ b/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_user_guest.yaml @@ -34,3 +34,13 @@ select_permissions: - meetingId: _eq: X-Hasura-ModeratorInMeeting allow_aggregations: true + - role: pre_join_bbb_client + permission: + columns: + - guestLobbyMessage + - guestStatus + - positionInWaitingQueue + filter: + userId: + _eq: X-Hasura-UserId + comment: "" diff --git a/bbb-graphql-server/metadata/query_collections.yaml b/bbb-graphql-server/metadata/query_collections.yaml index fe51488c70..d9a519af88 100644 --- a/bbb-graphql-server/metadata/query_collections.yaml +++ b/bbb-graphql-server/metadata/query_collections.yaml @@ -1 +1,20 @@ -[] +- name: allowed-queries + definition: + queries: + - name: UserCurrent + query: | + query UserCurrent { + user_current { + userId + name + guestStatus + guestStatusDetails { + guestLobbyMessage + positionInWaitingQueue + } + meeting { + name + logoutUrl + } + } + } diff --git a/bbb-graphql-server/metadata/rest_endpoints.yaml b/bbb-graphql-server/metadata/rest_endpoints.yaml index fe51488c70..61b6d717e2 100644 --- a/bbb-graphql-server/metadata/rest_endpoints.yaml +++ b/bbb-graphql-server/metadata/rest_endpoints.yaml @@ -1 +1,9 @@ -[] +- comment: "" + definition: + query: + collection_name: allowed-queries + query_name: UserCurrent + methods: + - GET + name: UserCurrent + url: usercurrent diff --git a/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js b/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js index 1a4cd08b83..c0c794bf25 100755 --- a/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js +++ b/bigbluebutton-html5/imports/api/meetings/server/modifiers/addMeeting.js @@ -131,6 +131,7 @@ export default async function addMeeting(meeting) { }, systemProps: { html5InstanceId: Number, + logoutUrl: String, }, groups: Array, overrideClientSettings: String, diff --git a/bigbluebutton-html5/imports/ui/core/graphql/queries/currentUserSubscription.ts b/bigbluebutton-html5/imports/ui/core/graphql/queries/currentUserSubscription.ts index cd47493dfb..246c2f6283 100644 --- a/bigbluebutton-html5/imports/ui/core/graphql/queries/currentUserSubscription.ts +++ b/bigbluebutton-html5/imports/ui/core/graphql/queries/currentUserSubscription.ts @@ -22,9 +22,7 @@ subscription userCurrentSubscription { emoji extId guest - guestStatus { - guestStatus - } + guestStatus hasDrawPermissionOnCurrentPage isDialIn isModerator diff --git a/bigbluebutton-html5/private/static/guest-wait/guest-wait.html b/bigbluebutton-html5/private/static/guest-wait/guest-wait.html index e4c1f899fa..26d83727fc 100755 --- a/bigbluebutton-html5/private/static/guest-wait/guest-wait.html +++ b/bigbluebutton-html5/private/static/guest-wait/guest-wait.html @@ -152,7 +152,7 @@ let positionInWaitingQueue = ''; function updatePositionInWaitingQueue(newPositionInWaitingQueue) { if (positionInWaitingQueue !== newPositionInWaitingQueue) { - positionInWaitingQueue = newPositionInWaitingQueue; + positionInWaitingQueue = newPositionInWaitingQueue.toString(); if (positionInWaitingQueue === '1') { updatePositionOnPage(_('app.guest.firstPositionInWaitingQueue'), ''); } else { @@ -173,6 +173,15 @@ return null; } + function getClientJoinUrl() { + const joinEndpoint = '/html5client/join'; + const sessionToken = getSearchParam('sessionToken'); + const url = new URL(`${window.location.origin}${joinEndpoint}`); + url.search = `sessionToken=${sessionToken}`; + + return url; + } + async function fetchLocalizedMessages() { const DEFAULT_LANGUAGE = 'en'; const LOCALES_ENDPOINT = '/html5client/locale'; @@ -264,43 +273,16 @@ const sessionToken = getSearchParam('sessionToken'); if (!sessionToken) { - disableAnimation() + disableAnimation(); updateMessage(_('app.guest.noSessionToken')); return; } - //First, check that we already have a response - const statusFromStorage = sessionStorage.getItem(`guestStatus_${sessionToken}`); - - if(statusFromStorage) { - stopUpdatingWaitingPosition(); - - const statusParsed = JSON.parse(statusFromStorage); - const { status, response } = statusParsed; - - if(status === 'ALLOW'){ - updateLobbyMessage(_('app.guest.allow')); - setTimeout(() => { - disableAnimation(); - window.location = response.url; - }, MESSAGE_TIMEOUT); - } else { - redirect( - _('app.guest.' + response.messageKey), - response.url, - ); - } - - return; - } - pollGuestStatus(sessionToken, 0); } catch (e) { disableAnimation(); console.error(e); updateMessage(_('app.guest.errorSeeConsole')); } - - }) .catch((e) => { console.error(e); @@ -308,15 +290,21 @@ }); } - function fetchGuestWait(sessionToken) { - const GUEST_WAIT_ENDPOINT = '/bigbluebutton/api/guestWait'; + function fetchUserCurrent(sessionToken) { + const GUEST_WAIT_ENDPOINT = '/api/rest/usercurrent/'; const url = new URL(`${window.location.origin}${GUEST_WAIT_ENDPOINT}`); - url.search = `sessionToken=${sessionToken}&redirect=false`; - return fetch(url, { method: 'get' }); + + const headers = new Headers({ + 'X-Session-Token': sessionToken, + 'Content-Type': 'application/json', + }); + + return fetch(url, { method: 'get', headers: headers }); }; function redirect(message, url) { disableAnimation(); + stopUpdatingWaitingPosition(); updateMessage(message); setTimeout(() => { window.location = url; @@ -326,47 +314,50 @@ function pollGuestStatus(token, everyMs) { setTimeout(function () { - fetchGuestWait(token) - .then(async (resp) => await resp.json()) - .then((data) => { - const code = data.response.returncode; + fetchUserCurrent(token) + .then(async (resp) => await resp.json()) + .then((data) => { + if(data.hasOwnProperty('error') || !data.hasOwnProperty('user_current')) { + disableAnimation(); + stopUpdatingWaitingPosition(); + updateMessage(_('app.guest.guestInvalid')); + return; + } + const userData = data.user_current[0]; - const response = data.response; + if (userData.guestStatus === 'ALLOW') { + updateLobbyMessage(_('app.guest.allow')); + stopUpdatingWaitingPosition(); - const saveStatusResponse = (status, response, token) => { - stopUpdatingWaitingPosition(); - sessionStorage.setItem(`guestStatus_${token}`, JSON.stringify({ status, response })); - }; + // Timeout is required by accessibility to allow viewing of the message for a minimum of 3 seconds + // before redirecting. + setTimeout(() => { + disableAnimation(); + window.location = getClientJoinUrl(); + }, MESSAGE_TIMEOUT); + return; + } - if (code === 'FAILED') { - saveStatusResponse(code, response, token); - return redirect(_('app.guest.' + data.response.messageKey), data.response.url); - } + if (userData.guestStatus === 'DENY') { + stopUpdatingWaitingPosition(); + return redirect(_('app.guest.guestDeny'), userData.meeting.logoutUrl); + } - const status = data.response.guestStatus; + if (userData.guestStatus === 'WAIT') { + //Wait message will be set by `updateLobbyMessage` + //updateMessage(updateMessage(_('app.guest.guestWait'))); + } - if (status === 'DENY') { - saveStatusResponse(status, response, token); - return redirect(_('app.guest.' + data.response.messageKey), data.response.url); - } + if(userData.guestStatusDetails !== null) { + updatePositionInWaitingQueue(userData.guestStatusDetails.positionInWaitingQueue); + updateLobbyMessage(userData.guestStatusDetails.guestLobbyMessage || ''); + } - if (status === 'ALLOW') { - updateLobbyMessage(_('app.guest.allow')); - saveStatusResponse(status, response, token); - // Timeout is required by accessibility to allow viewing of the message for a minimum of 3 seconds - // before redirecting. - setTimeout(() => { - disableAnimation(); - window.location = data.response.url; - }, MESSAGE_TIMEOUT); - return; - } - updatePositionInWaitingQueue(data.response.positionInWaitingQueue); - updateLobbyMessage(data.response.lobbyMessage); + const ATTEMPT_EVERY_MS = 10 * 1000; // 10 seconds + return pollGuestStatus(token, ATTEMPT_EVERY_MS); + + }); - const ATTEMPT_EVERY_MS = 10 * 1000; // 10 seconds - return pollGuestStatus(token, ATTEMPT_EVERY_MS); - }); }, everyMs); };