Merge pull request #20392 from dennis531/promote-guest-to-moderator

feat: Config for promoting guests to moderators
This commit is contained in:
Anton Georgiev 2024-06-04 09:24:15 -04:00 committed by GitHub
commit 55a9ec60ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 87 additions and 33 deletions

View File

@ -31,7 +31,7 @@ trait ChangeUserRoleCmdMsgHdlr extends RightsManagementTrait {
} yield {
RegisteredUsers.updateUserRole(liveMeeting.registeredUsers, u, userRole)
}
val promoteGuest = !liveMeeting.props.usersProp.authenticatedGuest
val promoteGuest = !liveMeeting.props.usersProp.authenticatedGuest || liveMeeting.props.usersProp.allowPromoteGuestToModerator
if (msg.body.role == Roles.MODERATOR_ROLE && (!uvo.guest || promoteGuest)) {
// Promote non-guest users.
val notifyEvent = MsgBuilder.buildNotifyUserInMeetingEvtMsg(

View File

@ -19,7 +19,8 @@ case class MeetingUsersPoliciesDbModel(
meetingLayout: String,
allowModsToUnmuteUsers: Boolean,
allowModsToEjectCameras: Boolean,
authenticatedGuest: Boolean
authenticatedGuest: Boolean,
allowPromoteGuestToModerator: Boolean
)
class MeetingUsersPoliciesDbTableDef(tag: Tag) extends Table[MeetingUsersPoliciesDbModel](tag, "meeting_usersPolicies") {
@ -34,10 +35,11 @@ class MeetingUsersPoliciesDbTableDef(tag: Tag) extends Table[MeetingUsersPolicie
val allowModsToUnmuteUsers = column[Boolean]("allowModsToUnmuteUsers")
val allowModsToEjectCameras = column[Boolean]("allowModsToEjectCameras")
val authenticatedGuest = column[Boolean]("authenticatedGuest")
val allowPromoteGuestToModerator = column[Boolean]("allowPromoteGuestToModerator")
// val fk_meetingId: ForeignKeyQuery[MeetingDbTableDef, MeetingDbModel] = foreignKey("fk_meetingId", meetingId, TableQuery[MeetingDbTableDef])(_.meetingId)
override val * : ProvenShape[MeetingUsersPoliciesDbModel] = (meetingId, maxUsers, maxUserConcurrentAccesses, webcamsOnlyForModerator, userCameraCap, guestPolicy, guestLobbyMessage, meetingLayout, allowModsToUnmuteUsers, allowModsToEjectCameras, authenticatedGuest) <> (MeetingUsersPoliciesDbModel.tupled, MeetingUsersPoliciesDbModel.unapply)
override val * : ProvenShape[MeetingUsersPoliciesDbModel] = (meetingId, maxUsers, maxUserConcurrentAccesses, webcamsOnlyForModerator, userCameraCap, guestPolicy, guestLobbyMessage, meetingLayout, allowModsToUnmuteUsers, allowModsToEjectCameras, authenticatedGuest, allowPromoteGuestToModerator) <> (MeetingUsersPoliciesDbModel.tupled, MeetingUsersPoliciesDbModel.unapply)
}
object MeetingUsersPoliciesDAO {
@ -56,6 +58,7 @@ object MeetingUsersPoliciesDAO {
allowModsToUnmuteUsers = usersProp.allowModsToUnmuteUsers,
allowModsToEjectCameras = usersProp.allowModsToEjectCameras,
authenticatedGuest = usersProp.authenticatedGuest,
allowPromoteGuestToModerator = usersProp.allowPromoteGuestToModerator,
)
)
).onComplete {

View File

@ -44,6 +44,7 @@ trait AppsTestFixtures {
val allowModsToUnmuteUsers = false
val allowModsToEjectCameras = false
val authenticatedGuest = false
val allowPromoteGuestToModerator = false
val meetingLayout = ""
val captureNotesFilename = s"Room 0${sequence} (Notes)"
val captureSlidesFilename = s"Room 0${sequence} (Whiteboard)"
@ -70,7 +71,7 @@ trait AppsTestFixtures {
val usersProp = UsersProp(maxUsers = maxUsers, webcamsOnlyForModerator = webcamsOnlyForModerator,
userCameraCap = userCameraCap,
guestPolicy = guestPolicy, allowModsToUnmuteUsers = allowModsToUnmuteUsers, allowModsToEjectCameras = allowModsToEjectCameras,
authenticatedGuest = authenticatedGuest, meetingLayout = meetingLayout)
authenticatedGuest = authenticatedGuest, allowPromoteGuestToModerator = allowPromoteGuestToModerator, meetingLayout = meetingLayout)
val metadataProp = new MetadataProp(metadata)
val defaultProps = DefaultProps(meetingProp, breakoutProps, durationProps, password, recordProp, welcomeProp, voiceProp,

View File

@ -41,15 +41,16 @@ case class WelcomeProp(welcomeMsgTemplate: String, welcomeMsg: String, modOnlyMe
case class VoiceProp(telVoice: String, voiceConf: String, dialNumber: String, muteOnStart: Boolean)
case class UsersProp(
maxUsers: Int,
maxUserConcurrentAccesses:Int,
webcamsOnlyForModerator: Boolean,
userCameraCap: Int,
guestPolicy: String,
meetingLayout: String,
allowModsToUnmuteUsers: Boolean,
allowModsToEjectCameras: Boolean,
authenticatedGuest: Boolean
maxUsers: Int,
maxUserConcurrentAccesses: Int,
webcamsOnlyForModerator: Boolean,
userCameraCap: Int,
guestPolicy: String,
meetingLayout: String,
allowModsToUnmuteUsers: Boolean,
allowModsToEjectCameras: Boolean,
authenticatedGuest: Boolean,
allowPromoteGuestToModerator: Boolean
)
case class MetadataProp(metadata: collection.immutable.Map[String, String])

View File

@ -44,6 +44,7 @@ trait TestFixtures {
val keepEvents = false
val guestPolicy = "ALWAYS_ASK"
val authenticatedGuest = false
val allowPromoteGuestToModerator = false
val metadata: collection.immutable.Map[String, String] = Map("foo" -> "bar", "bar" -> "baz", "baz" -> "foo")
val captureNotesFilename = s"Room 0${sequence} (Notes)"
val captureSlidesFilename = s"Room 0${sequence} (Whiteboard)"
@ -71,7 +72,7 @@ trait TestFixtures {
val voiceProp = VoiceProp(telVoice = voiceConfId, voiceConf = voiceConfId, dialNumber = dialNumber, muteOnStart = muteOnStart)
val usersProp = UsersProp(maxUsers = maxUsers, webcamsOnlyForModerator = webcamsOnlyForModerator,
userCameraCap = userCameraCap,
guestPolicy = guestPolicy, allowModsToUnmuteUsers = allowModsToUnmuteUsers, allowModsToEjectCameras = allowModsToEjectCameras, authenticatedGuest = authenticatedGuest)
guestPolicy = guestPolicy, allowModsToUnmuteUsers = allowModsToUnmuteUsers, allowModsToEjectCameras = allowModsToEjectCameras, authenticatedGuest = authenticatedGuest, allowPromoteGuestToModerator = allowPromoteGuestToModerator)
val metadataProp = new MetadataProp(metadata)
val defaultProps = DefaultProps(meetingProp, breakoutProps, durationProps, password, recordProp, welcomeProp, voiceProp,
usersProp, metadataProp)

View File

@ -33,6 +33,7 @@ public class ApiParams {
public static final String FREE_JOIN = "freeJoin";
public static final String FULL_NAME = "fullName";
public static final String GUEST_POLICY = "guestPolicy";
public static final String ALLOW_PROMOTE_GUEST_TO_MODERATOR = "allowPromoteGuestToModerator";
public static final String MEETING_LAYOUT = "meetingLayout";
public static final String IS_BREAKOUT = "isBreakout";
public static final String LOGO = "logo";

View File

@ -446,8 +446,8 @@ public class MeetingService implements MessageListener {
m.getWebcamsOnlyForModerator(), m.getMeetingCameraCap(), m.getUserCameraCap(), m.getMaxPinnedCameras(), m.getModeratorPassword(), m.getViewerPassword(),
m.getLearningDashboardAccessToken(), m.getCreateTime(),
formatPrettyDate(m.getCreateTime()), m.isBreakout(), m.getSequence(), m.isFreeJoin(), m.getMetadata(),
m.getGuestPolicy(), m.getAuthenticatedGuest(), m.getMeetingLayout(), m.getWelcomeMessageTemplate(), m.getWelcomeMessage(), m.getModeratorOnlyMessage(),
m.getDialNumber(), m.getMaxUsers(), m.getMaxUserConcurrentAccesses(),
m.getGuestPolicy(), m.getAuthenticatedGuest(), m.getAllowPromoteGuestToModerator(), m.getMeetingLayout(), m.getWelcomeMessageTemplate(), m.getWelcomeMessage(),
m.getModeratorOnlyMessage(), m.getDialNumber(), m.getMaxUsers(), m.getMaxUserConcurrentAccesses(),
m.getMeetingExpireIfNoUserJoinedInMinutes(), m.getMeetingExpireWhenLastUserLeftInMinutes(),
m.getUserInactivityInspectTimerInMinutes(), m.getUserInactivityThresholdInMinutes(),
m.getUserActivitySignResponseDelayInMinutes(), m.getEndWhenNoModerator(), m.getEndWhenNoModeratorDelayInMinutes(),

View File

@ -79,6 +79,7 @@ public class ParamsProcessorUtil {
private String defaultAvatarURL;
private String defaultGuestPolicy;
private Boolean authenticatedGuest;
private Boolean defaultAllowPromoteGuestToModerator;
private String defaultMeetingLayout;
private int defaultMeetingDuration;
private boolean disableRecordingDefault;
@ -672,6 +673,11 @@ public class ParamsProcessorUtil {
guestPolicy = params.get(ApiParams.GUEST_POLICY);
}
Boolean allowPromoteGuestToModerator = defaultAllowPromoteGuestToModerator;
if (!StringUtils.isEmpty(params.get(ApiParams.ALLOW_PROMOTE_GUEST_TO_MODERATOR))) {
allowPromoteGuestToModerator = Boolean.parseBoolean(params.get(ApiParams.ALLOW_PROMOTE_GUEST_TO_MODERATOR));
}
String presentationUploadExternalDescription = defaultPresentationUploadExternalDescription;
if (!StringUtils.isEmpty(params.get(ApiParams.PRESENTATION_UPLOAD_EXTERNAL_DESCRIPTION))) {
presentationUploadExternalDescription = params.get(ApiParams.PRESENTATION_UPLOAD_EXTERNAL_DESCRIPTION);
@ -760,6 +766,7 @@ public class ParamsProcessorUtil {
.withWelcomeMessage(welcomeMessage).isBreakout(isBreakout)
.withGuestPolicy(guestPolicy)
.withAuthenticatedGuest(authenticatedGuest)
.withAllowPromoteGuestToModerator(allowPromoteGuestToModerator)
.withAllowRequestsWithoutSession(allowRequestsWithoutSession)
.withMeetingLayout(meetingLayout)
.withBreakoutRoomsParams(breakoutParams)
@ -1290,6 +1297,10 @@ public class ParamsProcessorUtil {
this.authenticatedGuest = value;
}
public void setDefaultAllowPromoteGuestToModerator(Boolean value) {
this.defaultAllowPromoteGuestToModerator = value;
}
public void setDefaultMeetingLayout(String meetingLayout) {
this.defaultMeetingLayout = meetingLayout;
}

View File

@ -81,6 +81,7 @@ public class Meeting {
private String guestLobbyMessage = "";
private Map<String,String> usersWithGuestLobbyMessages;
private Boolean authenticatedGuest = false;
private Boolean allowPromoteGuestToModerator = false;
private String meetingLayout = MeetingLayout.SMART_LAYOUT;
private boolean userHasJoined = false;
private Map<String, String> guestUsersWithPositionInWaitingLine;
@ -165,6 +166,7 @@ public class Meeting {
isBreakout = builder.isBreakout;
guestPolicy = builder.guestPolicy;
authenticatedGuest = builder.authenticatedGuest;
allowPromoteGuestToModerator = builder.allowPromoteGuestToModerator;
meetingLayout = builder.meetingLayout;
allowRequestsWithoutSession = builder.allowRequestsWithoutSession;
breakoutRoomsParams = builder.breakoutRoomsParams;
@ -501,6 +503,14 @@ public class Meeting {
return authenticatedGuest;
}
public void setAllowPromoteGuestToModerator(Boolean value) {
allowPromoteGuestToModerator = value;
}
public Boolean getAllowPromoteGuestToModerator() {
return allowPromoteGuestToModerator;
}
public void setMeetingLayout(String layout) {
meetingLayout = layout;
}
@ -910,6 +920,7 @@ public class Meeting {
private boolean isBreakout;
private String guestPolicy;
private Boolean authenticatedGuest;
private Boolean allowPromoteGuestToModerator;
private Boolean allowRequestsWithoutSession;
private String meetingLayout;
private BreakoutRoomsParams breakoutRoomsParams;
@ -1096,6 +1107,11 @@ public class Meeting {
return this;
}
public Builder withAllowPromoteGuestToModerator(Boolean value) {
allowPromoteGuestToModerator = value;
return this;
}
public Builder withAllowRequestsWithoutSession(Boolean value) {
allowRequestsWithoutSession = value;
return this;

View File

@ -26,7 +26,7 @@ public interface IBbbWebApiGWApp {
Integer maxPinnedCameras,
String moderatorPass, String viewerPass, String learningDashboardAccessToken, Long createTime,
String createDate, Boolean isBreakout, Integer sequence, Boolean freejoin, Map<String, String> metadata,
String guestPolicy, Boolean authenticatedGuest, String meetingLayout, String welcomeMsgTemplate, String welcomeMsg, String modOnlyMessage,
String guestPolicy, Boolean authenticatedGuest, Boolean allowPromoteGuestToModerator, String meetingLayout, String welcomeMsgTemplate, String welcomeMsg, String modOnlyMessage,
String dialNumber, Integer maxUsers, Integer maxUserConcurrentAccesses,
Integer meetingExpireIfNoUserJoinedInMinutes,
Integer meetingExpireWhenLastUserLeftInMinutes,

View File

@ -7,6 +7,7 @@ public class UsersProp2 {
public final String guestPolicy;
public final String meetingLayout;
public final boolean authenticatedGuest;
public final boolean allowPromoteGuestToModerator;
public final boolean userHasJoined;
public final boolean webcamsOnlyForModerator;
public final int userCameraCap;
@ -21,6 +22,7 @@ public class UsersProp2 {
String guestPolicy,
String meetingLayout,
boolean authenticatedGuest,
boolean allowPromoteGuestToModerator,
boolean userHasJoined,
Map<String, String> userCustomData,
Map<String, User2> users,
@ -31,6 +33,7 @@ public class UsersProp2 {
this.guestPolicy = guestPolicy;
this.meetingLayout = meetingLayout;
this.authenticatedGuest = authenticatedGuest;
this.allowPromoteGuestToModerator = allowPromoteGuestToModerator;
this.userHasJoined = userHasJoined;
this.userCustomData = userCustomData;
this.users = users;

View File

@ -130,7 +130,8 @@ class BbbWebApiGWApp(
createTime: java.lang.Long, createDate: String, isBreakout: java.lang.Boolean,
sequence: java.lang.Integer,
freeJoin: java.lang.Boolean,
metadata: java.util.Map[String, String], guestPolicy: String, authenticatedGuest: java.lang.Boolean, meetingLayout: String,
metadata: java.util.Map[String, String], guestPolicy: String, authenticatedGuest: java.lang.Boolean, allowPromoteGuestToModerator: java.lang.Boolean,
meetingLayout: String,
welcomeMsgTemplate: String, welcomeMsg: String, modOnlyMessage: String,
dialNumber: String,
maxUsers: java.lang.Integer,
@ -216,7 +217,8 @@ class BbbWebApiGWApp(
userCameraCap = userCameraCap.intValue(),
guestPolicy = guestPolicy, meetingLayout = meetingLayout, allowModsToUnmuteUsers = allowModsToUnmuteUsers.booleanValue(),
allowModsToEjectCameras = allowModsToEjectCameras.booleanValue(),
authenticatedGuest = authenticatedGuest.booleanValue()
authenticatedGuest = authenticatedGuest.booleanValue(),
allowPromoteGuestToModerator = allowPromoteGuestToModerator.booleanValue()
)
val metadataProp = MetadataProp(mapAsScalaMap(metadata).toMap)

View File

@ -128,16 +128,17 @@ create view "v_meeting_voiceSettings" as select * from meeting_voice;
create table "meeting_usersPolicies" (
"meetingId" varchar(100) primary key references "meeting"("meetingId") ON DELETE CASCADE,
"maxUsers" integer,
"maxUserConcurrentAccesses" integer,
"webcamsOnlyForModerator" boolean,
"userCameraCap" integer,
"guestPolicy" varchar(100),
"guestLobbyMessage" text,
"meetingLayout" varchar(100),
"allowModsToUnmuteUsers" boolean,
"allowModsToEjectCameras" boolean,
"authenticatedGuest" boolean
"maxUsers" integer,
"maxUserConcurrentAccesses" integer,
"webcamsOnlyForModerator" boolean,
"userCameraCap" integer,
"guestPolicy" varchar(100),
"guestLobbyMessage" text,
"meetingLayout" varchar(100),
"allowModsToUnmuteUsers" boolean,
"allowModsToEjectCameras" boolean,
"authenticatedGuest" boolean,
"allowPromoteGuestToModerator" boolean
);
create index "idx_meeting_usersPolicies_meetingId" on "meeting_usersPolicies"("meetingId");
@ -153,6 +154,7 @@ SELECT "meeting_usersPolicies"."meetingId",
"meeting_usersPolicies"."allowModsToUnmuteUsers",
"meeting_usersPolicies"."allowModsToEjectCameras",
"meeting_usersPolicies"."authenticatedGuest",
"meeting_usersPolicies"."allowPromoteGuestToModerator",
"meeting"."isBreakout" is false "moderatorsCanMuteAudio",
"meeting"."isBreakout" is false and "meeting_usersPolicies"."allowModsToUnmuteUsers" is true "moderatorsCanUnmuteAudio"
FROM "meeting_usersPolicies"

View File

@ -8,6 +8,7 @@ select_permissions:
- allowModsToEjectCameras
- allowModsToUnmuteUsers
- authenticatedGuest
- allowPromoteGuestToModerator
- guestLobbyMessage
- guestPolicy
- maxUserConcurrentAccesses

View File

@ -48,6 +48,7 @@ export interface UsersPolicies {
allowModsToEjectCameras: boolean;
allowModsToUnmuteUsers: boolean;
authenticatedGuest: boolean;
allowPromoteGuestToModerator: boolean;
guestPolicy: string;
maxUserConcurrentAccesses: number;
maxUsers: number;

View File

@ -279,6 +279,7 @@ const getUsersProp = () => {
'usersPolicies.allowModsToUnmuteUsers': 1,
'usersPolicies.allowModsToEjectCameras': 1,
'usersPolicies.authenticatedGuest': 1,
'usersPolicies.allowPromoteGuestToModerator': 1,
},
},
);
@ -289,6 +290,7 @@ const getUsersProp = () => {
allowModsToUnmuteUsers: false,
allowModsToEjectCameras: false,
authenticatedGuest: false,
allowPromoteGuestToModerator: false,
};
};
@ -341,14 +343,18 @@ const getAvailableActions = (
&& !isSubjectUserModerator
&& !isDialInUser
&& !isBreakoutRoom
&& !(isSubjectUserGuest && usersProp.authenticatedGuest);
&& !(isSubjectUserGuest
&& usersProp.authenticatedGuest
&& !usersProp.allowPromoteGuestToModerator);
const allowedToDemote = amIModerator
&& !amISubjectUser
&& isSubjectUserModerator
&& !isDialInUser
&& !isBreakoutRoom
&& !(isSubjectUserGuest && usersProp.authenticatedGuest);
&& !(isSubjectUserGuest
&& usersProp.authenticatedGuest
&& !usersProp.allowPromoteGuestToModerator);
const allowedToChangeStatus = amISubjectUser && USER_STATUS_ENABLED;

View File

@ -20,6 +20,7 @@ subscription MeetingPermissions {
allowModsToEjectCameras
allowModsToUnmuteUsers
authenticatedGuest
allowPromoteGuestToModerator
guestPolicy
maxUserConcurrentAccesses
maxUsers

View File

@ -60,14 +60,14 @@ export const generateActionsPermissions = (
&& !isSubjectUserModerator
&& !isDialInUser
&& !isBreakout
&& !(isSubjectUserGuest && usersPolicies?.authenticatedGuest);
&& !(isSubjectUserGuest && usersPolicies?.authenticatedGuest && !usersPolicies?.allowPromoteGuestToModerator);
const allowedToDemote = amIModerator
&& !amISubjectUser
&& isSubjectUserModerator
&& !isDialInUser
&& !isBreakout
&& !(isSubjectUserGuest && usersPolicies?.authenticatedGuest);
&& !(isSubjectUserGuest && usersPolicies?.authenticatedGuest && !usersPolicies?.allowPromoteGuestToModerator);
const allowedToChangeStatus = amISubjectUser && USER_STATUS_ENABLED;

View File

@ -160,6 +160,9 @@ defaultGuestPolicy=ALWAYS_ACCEPT
# Enables or disables authenticated guest
authenticatedGuest=true
# Allows moderators to promote guests to moderators when authenticatedGuest is enabled
defaultAllowPromoteGuestToModerator=false
#---------------------------------------------------
# Default Meeting Layout
# Accepted values are the standard layouts: CUSTOM_LAYOUT, SMART_LAYOUT, PRESENTATION_FOCUS, VIDEO_FOCUS

View File

@ -165,6 +165,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<property name="defaultAvatarURL" value="${defaultAvatarURL}"/>
<property name="defaultGuestPolicy" value="${defaultGuestPolicy}"/>
<property name="authenticatedGuest" value="${authenticatedGuest}"/>
<property name="defaultAllowPromoteGuestToModerator" value="${defaultAllowPromoteGuestToModerator}"/>
<property name="defaultMeetingLayout" value="${defaultMeetingLayout}"/>
<property name="meetingExpireIfNoUserJoinedInMinutes" value="${meetingExpireIfNoUserJoinedInMinutes}"/>
<property name="meetingExpireWhenLastUserLeftInMinutes" value="${meetingExpireWhenLastUserLeftInMinutes}"/>