Merge pull request #20686 from gustavotrott/gql-server-auth-session
refactor (graphql): Big refactor on Graphql authentication
This commit is contained in:
commit
7a03136d02
@ -4,8 +4,8 @@ import org.apache.pekko.http.scaladsl.model._
|
|||||||
import org.apache.pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
|
import org.apache.pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
|
||||||
import org.apache.pekko.http.scaladsl.server.Directives._
|
import org.apache.pekko.http.scaladsl.server.Directives._
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.api.{ApiResponseFailure, ApiResponseSuccess, UserInfosApiMsg}
|
import org.bigbluebutton.core.api.{ ApiResponseFailure, ApiResponseSuccess, UserInfosApiMsg }
|
||||||
import org.bigbluebutton.service.{HealthzService, MeetingInfoService, PubSubReceiveStatus, PubSubSendStatus, RecordingDBSendStatus, UserInfoService}
|
import org.bigbluebutton.service.{ HealthzService, MeetingInfoService, PubSubReceiveStatus, PubSubSendStatus, RecordingDBSendStatus, UserInfoService }
|
||||||
import spray.json._
|
import spray.json._
|
||||||
|
|
||||||
import scala.concurrent._
|
import scala.concurrent._
|
||||||
@ -128,22 +128,19 @@ class ApiService(healthz: HealthzService, meetingInfoz: MeetingInfoService, user
|
|||||||
}
|
}
|
||||||
} ~
|
} ~
|
||||||
path("userInfo") {
|
path("userInfo") {
|
||||||
parameter(
|
(headerValueByName("x-session-token") & headerValueByName("user-agent")) { (sessionToken, userAgent) =>
|
||||||
"meetingId".as[String],
|
get {
|
||||||
"userId".as[String],
|
val entityFuture = userInfoService.getUserInfo(sessionToken).map {
|
||||||
) { (meetingId, userId) =>
|
case ApiResponseSuccess(msg, userInfos: UserInfosApiMsg) =>
|
||||||
get {
|
val responseMap = userInfoService.generateResponseMap(userInfos)
|
||||||
val entityFuture = userInfoService.getUserInfo(meetingId, userId).map {
|
userInfoService.createHttpResponse(StatusCodes.OK, responseMap)
|
||||||
case ApiResponseSuccess(msg, userInfos: UserInfosApiMsg) =>
|
|
||||||
val responseMap = userInfoService.generateResponseMap(userInfos)
|
|
||||||
userInfoService.createHttpResponse(StatusCodes.OK, responseMap)
|
|
||||||
|
|
||||||
case ApiResponseFailure(msg, arg) =>
|
case ApiResponseFailure(msg, arg) =>
|
||||||
userInfoService.createHttpResponse(StatusCodes.OK, Map("response" -> "unauthorized", "message" -> msg))
|
userInfoService.createHttpResponse(StatusCodes.OK, Map("response" -> "unauthorized", "message" -> msg))
|
||||||
}
|
|
||||||
|
|
||||||
complete(entityFuture)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
complete(entityFuture)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import java.util.concurrent.TimeUnit
|
|||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.db.{ DatabaseConnection, MeetingDAO }
|
import org.bigbluebutton.core.db.{ DatabaseConnection, MeetingDAO }
|
||||||
import org.bigbluebutton.core.domain.MeetingEndReason
|
import org.bigbluebutton.core.domain.MeetingEndReason
|
||||||
|
import org.bigbluebutton.core.models.Roles
|
||||||
import org.bigbluebutton.core.running.RunningMeeting
|
import org.bigbluebutton.core.running.RunningMeeting
|
||||||
import org.bigbluebutton.core.util.ColorPicker
|
import org.bigbluebutton.core.util.ColorPicker
|
||||||
import org.bigbluebutton.core2.RunningMeetings
|
import org.bigbluebutton.core2.RunningMeetings
|
||||||
@ -45,6 +46,8 @@ class BigBlueButtonActor(
|
|||||||
|
|
||||||
private val meetings = new RunningMeetings
|
private val meetings = new RunningMeetings
|
||||||
|
|
||||||
|
private var sessionTokens = new collection.immutable.HashMap[String, (String, String)] //sessionToken -> (meetingId, userId)
|
||||||
|
|
||||||
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
|
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
|
||||||
case e: Exception => {
|
case e: Exception => {
|
||||||
val sw: StringWriter = new StringWriter()
|
val sw: StringWriter = new StringWriter()
|
||||||
@ -79,13 +82,39 @@ class BigBlueButtonActor(
|
|||||||
case _ => // do nothing
|
case _ => // do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
def handleGetUserApiMsg(msg: GetUserApiMsg, actorRef: ActorRef): Unit = {
|
private def handleGetUserApiMsg(msg: GetUserApiMsg, actorRef: ActorRef): Unit = {
|
||||||
log.debug("RECEIVED GetUserApiMsg msg {}", msg)
|
log.debug("RECEIVED GetUserApiMsg msg {}", msg)
|
||||||
|
|
||||||
RunningMeetings.findWithId(meetings, msg.meetingId) match {
|
sessionTokens.get(msg.sessionToken) match {
|
||||||
case Some(m) =>
|
case Some(sessionTokenInfo) =>
|
||||||
m.actorRef forward (msg)
|
RunningMeetings.findWithId(meetings, sessionTokenInfo._1) match {
|
||||||
|
case Some(m) =>
|
||||||
|
m.actorRef forward (msg)
|
||||||
|
|
||||||
|
case None =>
|
||||||
|
//The meeting is ended, it will return some data just to confirm the session was valid
|
||||||
|
//The client can request data after the meeting is ended
|
||||||
|
val userInfos = Map(
|
||||||
|
"returncode" -> "SUCCESS",
|
||||||
|
"sessionToken" -> msg.sessionToken,
|
||||||
|
"meetingID" -> sessionTokenInfo._1,
|
||||||
|
"internalUserID" -> sessionTokenInfo._2,
|
||||||
|
"externMeetingID" -> "",
|
||||||
|
"externUserID" -> "",
|
||||||
|
"online" -> false,
|
||||||
|
"authToken" -> "",
|
||||||
|
"role" -> Roles.VIEWER_ROLE,
|
||||||
|
"guest" -> "false",
|
||||||
|
"guestStatus" -> "ALLOWED",
|
||||||
|
"moderator" -> false,
|
||||||
|
"presenter" -> false,
|
||||||
|
"hideViewersCursor" -> false,
|
||||||
|
"hideViewersAnnotation" -> false,
|
||||||
|
"hideUserList" -> false,
|
||||||
|
"webcamsOnlyForModerator" -> false
|
||||||
|
)
|
||||||
|
actorRef ! ApiResponseSuccess("Meeting is ended!", UserInfosApiMsg(userInfos))
|
||||||
|
}
|
||||||
case None =>
|
case None =>
|
||||||
actorRef ! ApiResponseFailure("Meeting not found!")
|
actorRef ! ApiResponseFailure("Meeting not found!")
|
||||||
}
|
}
|
||||||
@ -125,6 +154,10 @@ class BigBlueButtonActor(
|
|||||||
m <- RunningMeetings.findWithId(meetings, msg.header.meetingId)
|
m <- RunningMeetings.findWithId(meetings, msg.header.meetingId)
|
||||||
} yield {
|
} yield {
|
||||||
log.debug("FORWARDING Register user message")
|
log.debug("FORWARDING Register user message")
|
||||||
|
|
||||||
|
//Store sessionTokens and associate them with their respective meetingId + userId owners
|
||||||
|
sessionTokens += (msg.body.sessionToken -> (msg.body.meetingId, msg.body.intUserId))
|
||||||
|
|
||||||
m.actorRef forward (msg)
|
m.actorRef forward (msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,11 +242,15 @@ class BigBlueButtonActor(
|
|||||||
context.stop(m.actorRef)
|
context.stop(m.actorRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MeetingDAO.delete(msg.meetingId)
|
//Delay removal of session tokens and Graphql data once users might request some info after the meeting is ended
|
||||||
// MeetingDAO.setMeetingEnded(msg.meetingId)
|
context.system.scheduler.scheduleOnce(Duration.create(60, TimeUnit.MINUTES)) {
|
||||||
// Removing the meeting is enough, all other tables has "ON DELETE CASCADE"
|
log.debug("Removing Graphql data and session tokens. meetingID={}", msg.meetingId)
|
||||||
// UserDAO.softDeleteAllFromMeeting(msg.meetingId)
|
|
||||||
// MeetingRecordingDAO.updateStopped(msg.meetingId, "")
|
sessionTokens = sessionTokens.filter(sessionTokenInfo => sessionTokenInfo._2._1 != msg.meetingId)
|
||||||
|
|
||||||
|
//In Db, Removing the meeting is enough, all other tables has "ON DELETE CASCADE"
|
||||||
|
MeetingDAO.delete(msg.meetingId)
|
||||||
|
}
|
||||||
|
|
||||||
//Remove ColorPicker idx of the meeting
|
//Remove ColorPicker idx of the meeting
|
||||||
ColorPicker.reset(m.props.meetingProp.intId)
|
ColorPicker.reset(m.props.meetingProp.intId)
|
||||||
|
@ -141,7 +141,7 @@ case class UserEstablishedGraphqlConnectionInternalMsg(userId: String, clientTyp
|
|||||||
/**
|
/**
|
||||||
* API endpoint /userInfo to provide User Session Variables messages
|
* API endpoint /userInfo to provide User Session Variables messages
|
||||||
*/
|
*/
|
||||||
case class GetUserApiMsg(meetingId: String, userIntId: String)
|
case class GetUserApiMsg(sessionToken: String)
|
||||||
case class UserInfosApiMsg(infos: Map[String, Any])
|
case class UserInfosApiMsg(infos: Map[String, Any])
|
||||||
|
|
||||||
trait ApiResponse
|
trait ApiResponse
|
||||||
|
@ -2,7 +2,7 @@ package org.bigbluebutton.core.apps.users
|
|||||||
|
|
||||||
import org.apache.pekko.actor.ActorRef
|
import org.apache.pekko.actor.ActorRef
|
||||||
import org.bigbluebutton.core.api.{ ApiResponseFailure, ApiResponseSuccess, GetUserApiMsg, UserInfosApiMsg }
|
import org.bigbluebutton.core.api.{ ApiResponseFailure, ApiResponseSuccess, GetUserApiMsg, UserInfosApiMsg }
|
||||||
import org.bigbluebutton.core.models.{ RegisteredUsers, Roles, Users2x }
|
import org.bigbluebutton.core.models.{ RegisteredUser, RegisteredUsers, Roles, Users2x }
|
||||||
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, OutMsgRouter }
|
import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, OutMsgRouter }
|
||||||
import org.bigbluebutton.core2.MeetingStatus2x
|
import org.bigbluebutton.core2.MeetingStatus2x
|
||||||
|
|
||||||
@ -13,52 +13,52 @@ trait GetUserApiMsgHdlr extends HandlerHelpers {
|
|||||||
val outGW: OutMsgRouter
|
val outGW: OutMsgRouter
|
||||||
|
|
||||||
def handleGetUsersMeetingReqMsg(msg: GetUserApiMsg, actorRef: ActorRef): Unit = {
|
def handleGetUsersMeetingReqMsg(msg: GetUserApiMsg, actorRef: ActorRef): Unit = {
|
||||||
|
RegisteredUsers.findWithSessionToken(msg.sessionToken, liveMeeting.registeredUsers) match {
|
||||||
val userOption = RegisteredUsers.findWithUserId(msg.userIntId, liveMeeting.registeredUsers)
|
|
||||||
|
|
||||||
userOption match {
|
|
||||||
case Some(regUser) =>
|
case Some(regUser) =>
|
||||||
log.debug("replying GetUserApiMsg with success")
|
log.debug("replying GetUserApiMsg with success")
|
||||||
|
actorRef ! ApiResponseSuccess("User found!", UserInfosApiMsg(getUserInfoResponse(regUser)))
|
||||||
val isModerator = (regUser.role == Roles.MODERATOR_ROLE)
|
|
||||||
val isLocked = Users2x.findWithIntId(liveMeeting.users2x, regUser.id).exists(u => u.locked)
|
|
||||||
val userStateExists = Users2x.findWithIntId(liveMeeting.users2x, regUser.id).nonEmpty
|
|
||||||
|
|
||||||
val userIsOnline = regUser.joined && !regUser.loggedOut && userStateExists
|
|
||||||
|
|
||||||
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
|
||||||
|
|
||||||
var userInfos: Map[String, Any] = Map()
|
|
||||||
userInfos += ("returncode" -> "SUCCESS")
|
|
||||||
userInfos += ("meetingID" -> liveMeeting.props.meetingProp.intId)
|
|
||||||
userInfos += ("externMeetingID" -> liveMeeting.props.meetingProp.extId)
|
|
||||||
userInfos += ("externUserID" -> regUser.externId)
|
|
||||||
userInfos += ("internalUserID" -> regUser.id)
|
|
||||||
userInfos += ("online" -> userIsOnline)
|
|
||||||
userInfos += ("authToken" -> regUser.authToken)
|
|
||||||
userInfos += ("sessionToken" -> regUser.sessionToken)
|
|
||||||
userInfos += ("role" -> regUser.role)
|
|
||||||
userInfos += ("guest" -> regUser.guest)
|
|
||||||
userInfos += ("guestStatus" -> regUser.guestStatus)
|
|
||||||
userInfos += ("moderator" -> isModerator)
|
|
||||||
userInfos += ("presenter" -> Users2x.userIsInPresenterGroup(liveMeeting.users2x, regUser.id))
|
|
||||||
if (isModerator || !isLocked) {
|
|
||||||
userInfos += ("hideViewersCursor" -> false)
|
|
||||||
userInfos += ("hideViewersAnnotation" -> false)
|
|
||||||
userInfos += ("hideUserList" -> false)
|
|
||||||
userInfos += ("webcamsOnlyForModerator" -> false)
|
|
||||||
} else {
|
|
||||||
userInfos += ("hideViewersCursor" -> permissions.hideViewersCursor)
|
|
||||||
userInfos += ("hideViewersAnnotation" -> permissions.hideViewersAnnotation)
|
|
||||||
userInfos += ("hideUserList" -> permissions.hideUserList)
|
|
||||||
userInfos += ("webcamsOnlyForModerator" -> MeetingStatus2x.webcamsOnlyForModeratorEnabled(liveMeeting.status))
|
|
||||||
}
|
|
||||||
|
|
||||||
actorRef ! ApiResponseSuccess("User found!", UserInfosApiMsg(userInfos))
|
|
||||||
|
|
||||||
case None =>
|
case None =>
|
||||||
log.debug("User not found, sending failure message")
|
log.debug("User not found, sending failure message")
|
||||||
actorRef ! ApiResponseFailure("User not found", Map())
|
actorRef ! ApiResponseFailure("User not found", Map())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def getUserInfoResponse(regUser: RegisteredUser): Map[String, Any] = {
|
||||||
|
val isModerator = (regUser.role == Roles.MODERATOR_ROLE)
|
||||||
|
val isLocked = Users2x.findWithIntId(liveMeeting.users2x, regUser.id).exists(u => u.locked)
|
||||||
|
val userStateExists = Users2x.findWithIntId(liveMeeting.users2x, regUser.id).nonEmpty
|
||||||
|
|
||||||
|
val userIsOnline = regUser.joined && !regUser.loggedOut && !regUser.ejected && userStateExists
|
||||||
|
|
||||||
|
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
||||||
|
|
||||||
|
var userInfos: Map[String, Any] = Map()
|
||||||
|
userInfos += ("returncode" -> "SUCCESS")
|
||||||
|
userInfos += ("meetingID" -> liveMeeting.props.meetingProp.intId)
|
||||||
|
userInfos += ("externMeetingID" -> liveMeeting.props.meetingProp.extId)
|
||||||
|
userInfos += ("externUserID" -> regUser.externId)
|
||||||
|
userInfos += ("internalUserID" -> regUser.id)
|
||||||
|
userInfos += ("online" -> userIsOnline)
|
||||||
|
userInfos += ("authToken" -> regUser.authToken)
|
||||||
|
userInfos += ("sessionToken" -> regUser.sessionToken)
|
||||||
|
userInfos += ("role" -> regUser.role)
|
||||||
|
userInfos += ("guest" -> regUser.guest)
|
||||||
|
userInfos += ("guestStatus" -> regUser.guestStatus)
|
||||||
|
userInfos += ("moderator" -> isModerator)
|
||||||
|
userInfos += ("presenter" -> Users2x.userIsInPresenterGroup(liveMeeting.users2x, regUser.id))
|
||||||
|
if (isModerator || !isLocked) {
|
||||||
|
userInfos += ("hideViewersCursor" -> false)
|
||||||
|
userInfos += ("hideViewersAnnotation" -> false)
|
||||||
|
userInfos += ("hideUserList" -> false)
|
||||||
|
userInfos += ("webcamsOnlyForModerator" -> false)
|
||||||
|
} else {
|
||||||
|
userInfos += ("hideViewersCursor" -> permissions.hideViewersCursor)
|
||||||
|
userInfos += ("hideViewersAnnotation" -> permissions.hideViewersAnnotation)
|
||||||
|
userInfos += ("hideUserList" -> permissions.hideUserList)
|
||||||
|
userInfos += ("webcamsOnlyForModerator" -> MeetingStatus2x.webcamsOnlyForModeratorEnabled(liveMeeting.status))
|
||||||
|
}
|
||||||
|
|
||||||
|
userInfos
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ trait RegisterUserReqMsgHdlr {
|
|||||||
if (liveMeeting.props.usersProp.maxUserConcurrentAccesses > 0) {
|
if (liveMeeting.props.usersProp.maxUserConcurrentAccesses > 0) {
|
||||||
val userConcurrentAccesses = RegisteredUsers.findAllWithExternUserId(regUser.externId, liveMeeting.registeredUsers)
|
val userConcurrentAccesses = RegisteredUsers.findAllWithExternUserId(regUser.externId, liveMeeting.registeredUsers)
|
||||||
.filter(u => !u.loggedOut)
|
.filter(u => !u.loggedOut)
|
||||||
|
.filter(u => !u.ejected)
|
||||||
.sortWith((u1, u2) => u1.registeredOn > u2.registeredOn) //Remove older first
|
.sortWith((u1, u2) => u1.registeredOn > u2.registeredOn) //Remove older first
|
||||||
|
|
||||||
val userAvailableSlots = liveMeeting.props.usersProp.maxUserConcurrentAccesses - userConcurrentAccesses.length
|
val userAvailableSlots = liveMeeting.props.usersProp.maxUserConcurrentAccesses - userConcurrentAccesses.length
|
||||||
|
@ -34,6 +34,7 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
|
|||||||
val validationResult = for {
|
val validationResult = for {
|
||||||
_ <- checkIfUserGuestStatusIsAllowed(user)
|
_ <- checkIfUserGuestStatusIsAllowed(user)
|
||||||
_ <- checkIfUserIsBanned(user)
|
_ <- checkIfUserIsBanned(user)
|
||||||
|
_ <- checkIfUserEjected(user)
|
||||||
_ <- checkIfUserLoggedOut(user)
|
_ <- checkIfUserLoggedOut(user)
|
||||||
_ <- validateMaxParticipants(user)
|
_ <- validateMaxParticipants(user)
|
||||||
} yield user
|
} yield user
|
||||||
@ -104,6 +105,14 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def checkIfUserEjected(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
|
if (user.ejected) {
|
||||||
|
Left(("User had ejected", EjectReasonCode.EJECT_USER))
|
||||||
|
} else {
|
||||||
|
Right(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private def checkIfUserLoggedOut(user: RegisteredUser): Either[(String, String), Unit] = {
|
private def checkIfUserLoggedOut(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
if (user.loggedOut) {
|
if (user.loggedOut) {
|
||||||
Left(("User had logged out", EjectReasonCode.USER_LOGGED_OUT))
|
Left(("User had logged out", EjectReasonCode.USER_LOGGED_OUT))
|
||||||
|
@ -27,6 +27,7 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers {
|
|||||||
val validationResult = for {
|
val validationResult = for {
|
||||||
_ <- checkIfUserGuestStatusIsAllowed(user)
|
_ <- checkIfUserGuestStatusIsAllowed(user)
|
||||||
_ <- checkIfUserIsBanned(user)
|
_ <- checkIfUserIsBanned(user)
|
||||||
|
_ <- checkIfUserEjected(user)
|
||||||
_ <- checkIfUserLoggedOut(user)
|
_ <- checkIfUserLoggedOut(user)
|
||||||
_ <- validateMaxParticipants(user)
|
_ <- validateMaxParticipants(user)
|
||||||
} yield user
|
} yield user
|
||||||
@ -66,6 +67,14 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def checkIfUserEjected(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
|
if (user.ejected) {
|
||||||
|
Left(("User had ejected", EjectReasonCode.EJECT_USER))
|
||||||
|
} else {
|
||||||
|
Right(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private def checkIfUserLoggedOut(user: RegisteredUser): Either[(String, String), Unit] = {
|
private def checkIfUserLoggedOut(user: RegisteredUser): Either[(String, String), Unit] = {
|
||||||
if (user.loggedOut) {
|
if (user.loggedOut) {
|
||||||
Left(("User had logged out", EjectReasonCode.USER_LOGGED_OUT))
|
Left(("User had logged out", EjectReasonCode.USER_LOGGED_OUT))
|
||||||
|
@ -24,11 +24,12 @@ object RegisteredUsers {
|
|||||||
guestStatus,
|
guestStatus,
|
||||||
excludeFromDashboard,
|
excludeFromDashboard,
|
||||||
System.currentTimeMillis(),
|
System.currentTimeMillis(),
|
||||||
0,
|
lastAuthTokenValidatedOn = 0,
|
||||||
false,
|
graphqlConnected = false,
|
||||||
0,
|
graphqlDisconnectedOn = 0,
|
||||||
false,
|
joined = false,
|
||||||
false,
|
ejected = false,
|
||||||
|
banned = false,
|
||||||
enforceLayout,
|
enforceLayout,
|
||||||
customParameters,
|
customParameters,
|
||||||
loggedOut,
|
loggedOut,
|
||||||
@ -39,6 +40,10 @@ object RegisteredUsers {
|
|||||||
users.toVector.find(u => u.authToken == token)
|
users.toVector.find(u => u.authToken == token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def findWithSessionToken(sessionToken: String, users: RegisteredUsers): Option[RegisteredUser] = {
|
||||||
|
users.toVector.find(u => u.sessionToken == sessionToken)
|
||||||
|
}
|
||||||
|
|
||||||
def findAll(users: RegisteredUsers): Vector[RegisteredUser] = {
|
def findAll(users: RegisteredUsers): Vector[RegisteredUser] = {
|
||||||
users.toVector
|
users.toVector
|
||||||
}
|
}
|
||||||
@ -128,9 +133,10 @@ object RegisteredUsers {
|
|||||||
UserDAO.update(u)
|
UserDAO.update(u)
|
||||||
u
|
u
|
||||||
} else {
|
} else {
|
||||||
users.delete(ejectedUser.id)
|
val u = ejectedUser.modify(_.ejected).setTo(true)
|
||||||
UserDAO.softDelete(ejectedUser.meetingId, ejectedUser.id)
|
users.save(u)
|
||||||
ejectedUser
|
|
||||||
|
updateUserJoin(users, u, joined = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,6 +249,7 @@ case class RegisteredUser(
|
|||||||
graphqlConnected: Boolean,
|
graphqlConnected: Boolean,
|
||||||
graphqlDisconnectedOn: Long,
|
graphqlDisconnectedOn: Long,
|
||||||
joined: Boolean,
|
joined: Boolean,
|
||||||
|
ejected: Boolean,
|
||||||
banned: Boolean,
|
banned: Boolean,
|
||||||
enforceLayout: String,
|
enforceLayout: String,
|
||||||
customParameters: Map[String,String],
|
customParameters: Map[String,String],
|
||||||
|
@ -635,7 +635,7 @@ class MeetingActor(
|
|||||||
case m: SetCurrentPagePubMsg =>
|
case m: SetCurrentPagePubMsg =>
|
||||||
state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||||
updateUserLastActivity(m.header.userId)
|
updateUserLastActivity(m.header.userId)
|
||||||
case m: SetPageInfiniteWhiteboardPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
case m: SetPageInfiniteWhiteboardPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||||
case m: RemovePresentationPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
case m: RemovePresentationPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||||
case m: SetPresentationDownloadablePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
case m: SetPresentationDownloadablePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||||
case m: PresentationConversionUpdateSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
case m: PresentationConversionUpdateSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
|
||||||
@ -961,6 +961,7 @@ class MeetingActor(
|
|||||||
regUser <- RegisteredUsers.findAll(liveMeeting.registeredUsers)
|
regUser <- RegisteredUsers.findAll(liveMeeting.registeredUsers)
|
||||||
} yield {
|
} yield {
|
||||||
if (!regUser.loggedOut
|
if (!regUser.loggedOut
|
||||||
|
&& !regUser.ejected
|
||||||
&& regUser.guestStatus == GuestStatus.WAIT
|
&& regUser.guestStatus == GuestStatus.WAIT
|
||||||
&& !regUser.graphqlConnected
|
&& !regUser.graphqlConnected
|
||||||
&& regUser.graphqlDisconnectedOn != 0) {
|
&& regUser.graphqlDisconnectedOn != 0) {
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
package org.bigbluebutton.service
|
package org.bigbluebutton.service
|
||||||
|
|
||||||
import org.apache.pekko.actor.{ ActorRef, ActorSystem }
|
import org.apache.pekko.actor.{ActorRef, ActorSystem}
|
||||||
import org.apache.pekko.http.scaladsl.model.headers.RawHeader
|
import org.apache.pekko.http.scaladsl.model.headers.RawHeader
|
||||||
import org.apache.pekko.http.scaladsl.model.{ ContentTypes, HttpEntity, HttpResponse, StatusCode }
|
import org.apache.pekko.http.scaladsl.model.{ContentTypes, HttpEntity, HttpResponse, StatusCode}
|
||||||
import org.apache.pekko.pattern.ask
|
import org.apache.pekko.pattern.ask
|
||||||
import org.apache.pekko.pattern.AskTimeoutException
|
import org.apache.pekko.pattern.AskTimeoutException
|
||||||
import org.apache.pekko.util.Timeout
|
import org.apache.pekko.util.Timeout
|
||||||
import org.bigbluebutton.common2.util.JsonUtil
|
import org.bigbluebutton.common2.util.JsonUtil
|
||||||
import org.bigbluebutton.core.api.{ ApiResponse, ApiResponseFailure, GetUserApiMsg, UserInfosApiMsg }
|
import org.bigbluebutton.core.api.{ApiResponse, ApiResponseFailure, GetUserApiMsg, UserInfosApiMsg}
|
||||||
|
|
||||||
import scala.concurrent.duration.DurationInt
|
import scala.concurrent.duration.DurationInt
|
||||||
import scala.concurrent.{ ExecutionContextExecutor, Future }
|
import scala.concurrent.{ExecutionContextExecutor, Future}
|
||||||
|
|
||||||
|
|
||||||
object UserInfoService {
|
object UserInfoService {
|
||||||
@ -21,8 +21,8 @@ class UserInfoService(system: ActorSystem, bbbActor: ActorRef) {
|
|||||||
implicit def executionContext: ExecutionContextExecutor = system.dispatcher
|
implicit def executionContext: ExecutionContextExecutor = system.dispatcher
|
||||||
implicit val timeout: Timeout = 2 seconds
|
implicit val timeout: Timeout = 2 seconds
|
||||||
|
|
||||||
def getUserInfo(meetingId: String, userIntId: String): Future[ApiResponse] = {
|
def getUserInfo(sessionToken: String): Future[ApiResponse] = {
|
||||||
val future = bbbActor.ask(GetUserApiMsg(meetingId, userIntId)).mapTo[ApiResponse]
|
val future = bbbActor.ask(GetUserApiMsg(sessionToken)).mapTo[ApiResponse]
|
||||||
|
|
||||||
future.recover {
|
future.recover {
|
||||||
case e: AskTimeoutException => ApiResponseFailure("Request Timeout error")
|
case e: AskTimeoutException => ApiResponseFailure("Request Timeout error")
|
||||||
|
@ -13,10 +13,10 @@ import (
|
|||||||
// sessionVarsHookUrl is the authentication hook URL obtained from an environment variable.
|
// sessionVarsHookUrl is the authentication hook URL obtained from an environment variable.
|
||||||
var sessionVarsHookUrl = os.Getenv("BBB_GRAPHQL_MIDDLEWARE_SESSION_VARS_HOOK_URL")
|
var sessionVarsHookUrl = os.Getenv("BBB_GRAPHQL_MIDDLEWARE_SESSION_VARS_HOOK_URL")
|
||||||
|
|
||||||
func AkkaAppsGetSessionVariablesFrom(browserConnectionId string, meetingId string, userId string) (map[string]string, error) {
|
func AkkaAppsGetSessionVariablesFrom(browserConnectionId string, sessionToken string) (map[string]string, error) {
|
||||||
logger := log.WithField("_routine", "BBBWebClient").WithField("browserConnectionId", browserConnectionId)
|
logger := log.WithField("_routine", "AkkaAppsClient").WithField("browserConnectionId", browserConnectionId)
|
||||||
logger.Debug("Starting BBBWebClient")
|
logger.Debug("Starting AkkaAppsClient")
|
||||||
defer logger.Debug("Finished BBBWebClient")
|
defer logger.Debug("Finished AkkaAppsClient")
|
||||||
|
|
||||||
// Create a new HTTP client with a cookie jar.
|
// Create a new HTTP client with a cookie jar.
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
@ -26,15 +26,17 @@ func AkkaAppsGetSessionVariablesFrom(browserConnectionId string, meetingId strin
|
|||||||
return nil, fmt.Errorf("BBB_GRAPHQL_MIDDLEWARE_SESSION_VARS_HOOK_URL not set")
|
return nil, fmt.Errorf("BBB_GRAPHQL_MIDDLEWARE_SESSION_VARS_HOOK_URL not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace("Get user session vars from: " + sessionVarsHookUrl + "?meetingId=" + meetingId + "&userId=" + userId)
|
log.Trace("Get user session vars from: " + sessionVarsHookUrl + "?sessionToken=" + sessionToken)
|
||||||
|
|
||||||
// Create a new HTTP request to the authentication hook URL.
|
// Create a new HTTP request to the authentication hook URL.
|
||||||
req, err := http.NewRequest("GET", sessionVarsHookUrl+"?meetingId="+meetingId+"&userId="+userId, nil)
|
req, err := http.NewRequest("GET", sessionVarsHookUrl, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the HTTP request to obtain user session variables (like X-Hasura-Role)
|
// Execute the HTTP request to obtain user session variables (like X-Hasura-Role)
|
||||||
|
req.Header.Set("x-session-token", sessionToken)
|
||||||
|
req.Header.Set("User-Agent", "bbb-graphql-middleware")
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
package bbb_web
|
package bbb_web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// authHookUrl is the authentication hook URL obtained from an environment variable.
|
// authHookUrl is the authentication hook URL obtained from an environment variable.
|
||||||
@ -41,7 +43,8 @@ func BBBWebCheckAuthorization(browserConnectionId string, sessionToken string, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Execute the HTTP request to obtain user session variables (like X-Hasura-Role)
|
// Execute the HTTP request to obtain user session variables (like X-Hasura-Role)
|
||||||
req.Header.Set("x-original-uri", authHookUrl+"?sessionToken="+sessionToken)
|
//req.Header.Set("x-original-uri", authHookUrl+"?sessionToken="+sessionToken)
|
||||||
|
req.Header.Set("x-session-token", sessionToken)
|
||||||
//req.Header.Set("User-Agent", "hasura-graphql-engine")
|
//req.Header.Set("User-Agent", "hasura-graphql-engine")
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -54,27 +57,44 @@ func BBBWebCheckAuthorization(browserConnectionId string, sessionToken string, c
|
|||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
var respBodyAsString = string(respBody)
|
log.Trace(string(respBody))
|
||||||
|
|
||||||
if respBodyAsString != "authorized" {
|
var respBodyAsMap map[string]string
|
||||||
return "", "", fmt.Errorf("auth token not authorized")
|
if err := json.Unmarshal(respBody, &respBodyAsMap); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the response status.
|
||||||
|
response, ok := respBodyAsMap["response"]
|
||||||
|
if !ok {
|
||||||
|
return "", "", fmt.Errorf("response key not found in the parsed object")
|
||||||
|
}
|
||||||
|
if response != "authorized" {
|
||||||
|
logger.Error(response)
|
||||||
|
return "", "", fmt.Errorf("user not authorized")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize the response header keys.
|
||||||
|
normalizedResponse := make(map[string]string)
|
||||||
|
for key, value := range respBodyAsMap {
|
||||||
|
if strings.HasPrefix(strings.ToLower(key), "x-") {
|
||||||
|
normalizedResponse[strings.ToLower(key)] = value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var userId string
|
var userId string
|
||||||
var meetingId string
|
var meetingId string
|
||||||
|
|
||||||
//Get userId and meetingId from response Header
|
//Get userId and meetingId from response Header
|
||||||
for key, values := range resp.Header {
|
for key, value := range normalizedResponse {
|
||||||
for _, value := range values {
|
log.Debug("%s: %s\n", key, value)
|
||||||
log.Debug("%s: %s\n", key, value)
|
|
||||||
|
|
||||||
if key == "User-Id" {
|
if key == "x-userid" {
|
||||||
userId = value
|
userId = value
|
||||||
}
|
}
|
||||||
|
|
||||||
if key == "Meeting-Id" {
|
if key == "x-meetingid" {
|
||||||
meetingId = value
|
meetingId = value
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,13 +240,12 @@ func invalidateHasuraConnectionForSessionToken(bc *common.BrowserConnection, ses
|
|||||||
|
|
||||||
func refreshUserSessionVariables(browserConnection *common.BrowserConnection) error {
|
func refreshUserSessionVariables(browserConnection *common.BrowserConnection) error {
|
||||||
BrowserConnectionsMutex.RLock()
|
BrowserConnectionsMutex.RLock()
|
||||||
browserUserId := browserConnection.UserId
|
sessionToken := browserConnection.SessionToken
|
||||||
browserMeetingId := browserConnection.MeetingId
|
|
||||||
browserConnectionId := browserConnection.Id
|
browserConnectionId := browserConnection.Id
|
||||||
BrowserConnectionsMutex.RUnlock()
|
BrowserConnectionsMutex.RUnlock()
|
||||||
|
|
||||||
// Check authorization
|
// Check authorization
|
||||||
sessionVariables, err := akka_apps.AkkaAppsGetSessionVariablesFrom(browserConnectionId, browserMeetingId, browserUserId)
|
sessionVariables, err := akka_apps.AkkaAppsGetSessionVariablesFrom(browserConnectionId, sessionToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return fmt.Errorf("error on checking sessionToken authorization")
|
return fmt.Errorf("error on checking sessionToken authorization")
|
||||||
|
@ -272,8 +272,8 @@ type Mutation {
|
|||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
pluginLearningAnalyticsDashboardSendGenericData(
|
pluginLearningAnalyticsDashboardSendGenericData(
|
||||||
genericDataForLearningAnalyticsDashboard: json!,
|
genericDataForLearningAnalyticsDashboard: json!
|
||||||
pluginName: String!,
|
pluginName: String!
|
||||||
): Boolean
|
): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,23 +1,6 @@
|
|||||||
- name: allowed-queries
|
- name: allowed-queries
|
||||||
definition:
|
definition:
|
||||||
queries:
|
queries:
|
||||||
- name: UserCurrent
|
|
||||||
query: |
|
|
||||||
query UserCurrent {
|
|
||||||
user_current {
|
|
||||||
userId
|
|
||||||
name
|
|
||||||
guestStatus
|
|
||||||
guestStatusDetails {
|
|
||||||
guestLobbyMessage
|
|
||||||
positionInWaitingQueue
|
|
||||||
}
|
|
||||||
meeting {
|
|
||||||
name
|
|
||||||
logoutUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
- name: clientStartupSettings
|
- name: clientStartupSettings
|
||||||
query: |
|
query: |
|
||||||
query clientStartupSettings {
|
query clientStartupSettings {
|
||||||
|
@ -16,12 +16,3 @@
|
|||||||
- GET
|
- GET
|
||||||
name: clientStartupSettings
|
name: clientStartupSettings
|
||||||
url: clientStartupSettings
|
url: clientStartupSettings
|
||||||
- comment: ""
|
|
||||||
definition:
|
|
||||||
query:
|
|
||||||
collection_name: allowed-queries
|
|
||||||
query_name: UserCurrent
|
|
||||||
methods:
|
|
||||||
- GET
|
|
||||||
name: UserCurrent
|
|
||||||
url: usercurrent
|
|
||||||
|
@ -76,8 +76,14 @@ const StartupDataFetch: React.FC<StartupDataFetchProps> = ({
|
|||||||
},
|
},
|
||||||
}).then((resp) => resp.json())
|
}).then((resp) => resp.json())
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
const url = `${data.response.graphqlApiUrl}/clientStartupSettings/?sessionToken=${sessionToken}`;
|
const url = `${data.response.graphqlApiUrl}/clientStartupSettings`;
|
||||||
fetch(url, { method: 'get', credentials: 'include' })
|
fetch(url, {
|
||||||
|
method: 'get',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'x-session-token': sessionToken,
|
||||||
|
},
|
||||||
|
})
|
||||||
.then((resp) => resp.json())
|
.then((resp) => resp.json())
|
||||||
.then((data: Response) => {
|
.then((data: Response) => {
|
||||||
const settings = data.meeting_clientSettings[0];
|
const settings = data.meeting_clientSettings[0];
|
||||||
|
@ -40,9 +40,14 @@ const SettingsLoader: React.FC = () => {
|
|||||||
.then((data) => {
|
.then((data) => {
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
const sessionToken = urlParams.get('sessionToken');
|
const sessionToken = urlParams.get('sessionToken');
|
||||||
const clientStartupSettings = `/clientSettings/?sessionToken=${sessionToken}`;
|
const url = new URL(`${data.response.graphqlApiUrl}/clientSettings`);
|
||||||
const url = new URL(`${data.response.graphqlApiUrl}${clientStartupSettings}`);
|
fetch(url, {
|
||||||
fetch(url, { method: 'get', credentials: 'include' })
|
method: 'get',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'x-session-token': sessionToken,
|
||||||
|
},
|
||||||
|
})
|
||||||
.then((resp) => resp.json())
|
.then((resp) => resp.json())
|
||||||
.then((data: Response) => {
|
.then((data: Response) => {
|
||||||
const settings = data?.meeting_clientSettings[0].clientSettingsJson;
|
const settings = data?.meeting_clientSettings[0].clientSettingsJson;
|
||||||
|
@ -62,72 +62,51 @@ class ConnectionController {
|
|||||||
|
|
||||||
def checkGraphqlAuthorization = {
|
def checkGraphqlAuthorization = {
|
||||||
try {
|
try {
|
||||||
if(!request.getHeader("User-Agent").startsWith('hasura-graphql-engine')) {
|
|
||||||
throw new Exception("Invalid User Agent")
|
|
||||||
}
|
|
||||||
|
|
||||||
String sessionToken = request.getHeader("x-session-token")
|
String sessionToken = request.getHeader("x-session-token")
|
||||||
|
|
||||||
UserSession userSession = meetingService.getUserSessionWithSessionToken(sessionToken)
|
UserSession userSession = meetingService.getUserSessionWithSessionToken(sessionToken)
|
||||||
Boolean allowRequestsWithoutSession = meetingService.getAllowRequestsWithoutSession(sessionToken)
|
Boolean isSessionTokenValid = session[sessionToken] != null
|
||||||
Boolean isSessionTokenInvalid = !session[sessionToken] && !allowRequestsWithoutSession
|
|
||||||
|
|
||||||
response.addHeader("Cache-Control", "no-cache")
|
response.addHeader("Cache-Control", "no-cache")
|
||||||
|
|
||||||
if (userSession != null && !isSessionTokenInvalid) {
|
if (userSession != null && isSessionTokenValid) {
|
||||||
Meeting m = meetingService.getMeeting(userSession.meetingID)
|
Meeting m = meetingService.getMeeting(userSession.meetingID)
|
||||||
User u
|
User u
|
||||||
if(m) {
|
if(m) {
|
||||||
u = m.getUserById(userSession.internalUserId)
|
u = m.getUserById(userSession.internalUserId)
|
||||||
}
|
}
|
||||||
|
|
||||||
Boolean cursorLocked = false
|
response.addHeader("Meeting-Id", userSession.meetingID)
|
||||||
Boolean annotationsLocked = false
|
|
||||||
Boolean userListLocked = false
|
|
||||||
Boolean webcamOnlyForMod = false
|
|
||||||
if(u && u.isLocked() && !u.isModerator()) {
|
|
||||||
cursorLocked = m.lockSettingsParams.hideViewersCursor
|
|
||||||
annotationsLocked = m.lockSettingsParams.hideViewersAnnotation
|
|
||||||
userListLocked = m.lockSettingsParams.hideUserList
|
|
||||||
webcamOnlyForMod = m.getWebcamsOnlyForModerator()
|
|
||||||
}
|
|
||||||
|
|
||||||
response.setStatus(200)
|
response.setStatus(200)
|
||||||
withFormat {
|
withFormat {
|
||||||
json {
|
json {
|
||||||
def builder = new JsonBuilder()
|
def builder = new JsonBuilder()
|
||||||
builder {
|
builder {
|
||||||
"response" "authorized"
|
"response" "authorized"
|
||||||
"X-Hasura-Role" m && u && !u.hasLeft() ? "bbb_client" : "not_joined_bbb_client"
|
"X-Currently-Online" m && u && !u.hasLeft() ? "true" : "false"
|
||||||
"X-Hasura-ModeratorInMeeting" u && u.isModerator() ? userSession.meetingID : ""
|
"X-Moderator" u && u.isModerator() ? "true" : "false"
|
||||||
"X-Hasura-PresenterInMeeting" u && u.isPresenter() ? userSession.meetingID : ""
|
"X-Presenter" u && u.isPresenter() ? "true" : "false"
|
||||||
"X-Hasura-UserId" userSession.internalUserId
|
"X-UserId" userSession.internalUserId
|
||||||
"X-Hasura-MeetingId" userSession.meetingID
|
"X-MeetingId" userSession.meetingID
|
||||||
"X-Hasura-CursorNotLockedInMeeting" cursorLocked ? "" : userSession.meetingID
|
|
||||||
"X-Hasura-CursorLockedUserId" cursorLocked ? userSession.internalUserId : ""
|
|
||||||
"X-Hasura-AnnotationsNotLockedInMeeting" annotationsLocked ? "" : userSession.meetingID
|
|
||||||
"X-Hasura-AnnotationsLockedUserId" annotationsLocked ? userSession.internalUserId : ""
|
|
||||||
"X-Hasura-UserListNotLockedInMeeting" userListLocked ? "" : userSession.meetingID
|
|
||||||
"X-Hasura-WebcamsNotLockedInMeeting" webcamOnlyForMod ? "" : userSession.meetingID
|
|
||||||
"X-Hasura-WebcamsLockedUserId" webcamOnlyForMod ? userSession.internalUserId : ""
|
|
||||||
}
|
}
|
||||||
render(contentType: "application/json", text: builder.toPrettyString())
|
render(contentType: "application/json", text: builder.toPrettyString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if(isSessionTokenValid) {
|
||||||
UserSessionBasicData removedUserSession = meetingService.getRemovedUserSessionWithSessionToken(sessionToken)
|
UserSessionBasicData removedUserSession = meetingService.getRemovedUserSessionWithSessionToken(sessionToken)
|
||||||
if(removedUserSession) {
|
if(removedUserSession) {
|
||||||
|
response.addHeader("Meeting-Id", removedUserSession.meetingId)
|
||||||
response.setStatus(200)
|
response.setStatus(200)
|
||||||
withFormat {
|
withFormat {
|
||||||
json {
|
json {
|
||||||
def builder = new JsonBuilder()
|
def builder = new JsonBuilder()
|
||||||
builder {
|
builder {
|
||||||
"response" "authorized"
|
"response" "authorized"
|
||||||
"X-Hasura-Role" "not_joined_bbb_client"
|
"X-Currently-Online" "false"
|
||||||
"X-Hasura-ModeratorInMeeting" removedUserSession.isModerator() ? removedUserSession.meetingId : ""
|
"X-Moderator" removedUserSession.isModerator() ? "true" : "false"
|
||||||
"X-Hasura-PresenterInMeeting" ""
|
"X-Presenter" "false"
|
||||||
"X-Hasura-UserId" removedUserSession.userId
|
"X-UserId" removedUserSession.userId
|
||||||
"X-Hasura-MeetingId" removedUserSession.meetingId
|
"X-MeetingId" removedUserSession.meetingId
|
||||||
}
|
}
|
||||||
render(contentType: "application/json", text: builder.toPrettyString())
|
render(contentType: "application/json", text: builder.toPrettyString())
|
||||||
}
|
}
|
||||||
@ -135,6 +114,8 @@ class ConnectionController {
|
|||||||
} else {
|
} else {
|
||||||
throw new Exception("Invalid User Session")
|
throw new Exception("Invalid User Session")
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
throw new Exception("Invalid sessionToken")
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.debug("Error while authenticating graphql connection: " + e.getMessage())
|
log.debug("Error while authenticating graphql connection: " + e.getMessage())
|
||||||
|
@ -4,7 +4,7 @@ BBB_GRAPHQL_MIDDLEWARE_REDIS_ADDRESS=127.0.0.1:6379
|
|||||||
BBB_GRAPHQL_MIDDLEWARE_REDIS_PASSWORD=
|
BBB_GRAPHQL_MIDDLEWARE_REDIS_PASSWORD=
|
||||||
BBB_GRAPHQL_MIDDLEWARE_HASURA_WS=ws://127.0.0.1:8085/v1/graphql
|
BBB_GRAPHQL_MIDDLEWARE_HASURA_WS=ws://127.0.0.1:8085/v1/graphql
|
||||||
BBB_GRAPHQL_MIDDLEWARE_MAX_CONN_PER_SECOND=10
|
BBB_GRAPHQL_MIDDLEWARE_MAX_CONN_PER_SECOND=10
|
||||||
BBB_GRAPHQL_MIDDLEWARE_AUTH_HOOK_URL=http://127.0.0.1:8090/bigbluebutton/connection/checkAuthorization
|
BBB_GRAPHQL_MIDDLEWARE_AUTH_HOOK_URL=http://127.0.0.1:8090/bigbluebutton/connection/checkGraphqlAuthorization
|
||||||
BBB_GRAPHQL_MIDDLEWARE_SESSION_VARS_HOOK_URL=http://127.0.0.1:8901/userInfo
|
BBB_GRAPHQL_MIDDLEWARE_SESSION_VARS_HOOK_URL=http://127.0.0.1:8901/userInfo
|
||||||
BBB_GRAPHQL_MIDDLEWARE_GRAPHQL_ACTIONS_URL=http://127.0.0.1:8093
|
BBB_GRAPHQL_MIDDLEWARE_GRAPHQL_ACTIONS_URL=http://127.0.0.1:8093
|
||||||
|
|
||||||
|
@ -25,20 +25,9 @@ location /api/rest {
|
|||||||
|
|
||||||
#Set cache system for client settings
|
#Set cache system for client settings
|
||||||
location ~ ^/api/rest/(clientStartupSettings|clientSettings)/ {
|
location ~ ^/api/rest/(clientStartupSettings|clientSettings)/ {
|
||||||
#Store URL sessionToken once it will be injected as a header X-Session-Token
|
auth_request /bigbluebutton/connection/checkGraphqlAuthorization;
|
||||||
set $session_token "";
|
|
||||||
if ($args ~* "(?:^|&)sessionToken=([^&]+)") {
|
|
||||||
set $session_token $1;
|
|
||||||
}
|
|
||||||
auth_request /bigbluebutton/connection/checkAuthorization;
|
|
||||||
auth_request_set $meeting_id $sent_http_meeting_id;
|
auth_request_set $meeting_id $sent_http_meeting_id;
|
||||||
|
|
||||||
#Remove sessionToken from URL once Hasura doesn't expect to receive it
|
|
||||||
if ($args ~* "^(.*&)?sessionToken=[^&]*(.*)$") {
|
|
||||||
set $args $1$2;
|
|
||||||
}
|
|
||||||
|
|
||||||
proxy_set_header x-session-token $session_token;
|
|
||||||
proxy_cache client_settings_cache;
|
proxy_cache client_settings_cache;
|
||||||
proxy_cache_key "$uri|$meeting_id";
|
proxy_cache_key "$uri|$meeting_id";
|
||||||
proxy_cache_use_stale updating;
|
proxy_cache_use_stale updating;
|
||||||
|
@ -11,5 +11,5 @@ HASURA_GRAPHQL_SERVER_PORT=8085
|
|||||||
HASURA_GRAPHQL_ADMIN_SECRET=bigbluebutton
|
HASURA_GRAPHQL_ADMIN_SECRET=bigbluebutton
|
||||||
HASURA_GRAPHQL_ENABLE_TELEMETRY=false
|
HASURA_GRAPHQL_ENABLE_TELEMETRY=false
|
||||||
HASURA_GRAPHQL_WEBSOCKET_KEEPALIVE=10
|
HASURA_GRAPHQL_WEBSOCKET_KEEPALIVE=10
|
||||||
HASURA_GRAPHQL_AUTH_HOOK=http://127.0.0.1:8090/bigbluebutton/connection/checkGraphqlAuthorization
|
HASURA_GRAPHQL_AUTH_HOOK=http://127.0.0.1:8901/userInfo
|
||||||
HASURA_BBB_GRAPHQL_ACTIONS_ADAPTER_URL=http://127.0.0.1:8093
|
HASURA_BBB_GRAPHQL_ACTIONS_ADAPTER_URL=http://127.0.0.1:8093
|
||||||
|
Loading…
Reference in New Issue
Block a user