From 03598be668ef9f03e1ef8b41d02cc7961d5feda7 Mon Sep 17 00:00:00 2001 From: Ahmad Farhat Date: Wed, 18 Oct 2023 15:04:48 -0400 Subject: [PATCH 01/17] Fix for superadmins when changing pages (#5470) * Fix for superadmins when changing pages * eslint --- app/controllers/application_controller.rb | 1 + app/javascript/routes/AuthenticatedOnly.jsx | 9 ++++++-- config/initializers/session_store.rb | 23 +++++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 config/initializers/session_store.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 69093e9be1..69795c8d4c 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -78,6 +78,7 @@ def append_info_to_payload(payload) def invalid_session?(user) return true if user&.session_token != session[:session_token] return true if user&.session_expiry && DateTime.now > user&.session_expiry + return true if !user.super_admin? && user.provider != current_provider false end diff --git a/app/javascript/routes/AuthenticatedOnly.jsx b/app/javascript/routes/AuthenticatedOnly.jsx index 4f5c5654e8..f8fde5ed10 100644 --- a/app/javascript/routes/AuthenticatedOnly.jsx +++ b/app/javascript/routes/AuthenticatedOnly.jsx @@ -27,7 +27,8 @@ export default function AuthenticatedOnly() { const { t } = useTranslation(); const currentUser = useAuth(); const location = useLocation(); - const match = useMatch('/rooms/:friendlyId'); + const roomsMatch = useMatch('/rooms/:friendlyId'); + const superAdminMatch = useMatch('/admin/*'); const deleteSession = useDeleteSession({ showToast: false }); // User is either pending or banned @@ -44,10 +45,14 @@ export default function AuthenticatedOnly() { } // Custom logic to redirect from Rooms page to join page if the user isn't signed in - if (!currentUser.signed_in && match) { + if (!currentUser.signed_in && roomsMatch) { return ; } + if (currentUser.signed_in && currentUser.isSuperAdmin && !superAdminMatch) { + return ; + } + if (!currentUser.signed_in) { toast.error(t('toast.error.signin_required')); return ; diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb new file mode 100644 index 0000000000..10565e2b03 --- /dev/null +++ b/config/initializers/session_store.rb @@ -0,0 +1,23 @@ +# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/. +# +# Copyright (c) 2022 BigBlueButton Inc. and by respective authors (see below). +# +# This program is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free Software +# Foundation; either version 3.0 of the License, or (at your option) any later +# version. +# +# Greenlight is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License along +# with Greenlight; if not, see . + +# frozen_string_literal: true + +if ENV['LOADBALANCER_ENDPOINT'].present? + Rails.application.config.session_store :cookie_store, key: '_greenlight-3_0_session', domain: ENV.fetch('SESSION_DOMAIN_NAME', nil) +else + Rails.application.config.session_store :cookie_store, key: '_greenlight-3_0_session' +end From a376a7fd00cf4da656beb81e98e54d9b8caa511c Mon Sep 17 00:00:00 2001 From: Jan Kessler Date: Mon, 30 Oct 2023 14:52:47 +0100 Subject: [PATCH 02/17] Add env EXTERNAL_AUTH and replace checks for OPENID_CONNECT with checks for EXTERNAL_AUTH where applicable (#5480) * add env EXTERNAL_AUTH and replace env OPENID_CONNECT with it where applicable * remove OPENID_CONNECT from envAPI --- app/controllers/api/v1/api_controller.rb | 2 +- app/controllers/api/v1/env_controller.rb | 2 +- app/javascript/components/admin/manage_users/ManageUsers.jsx | 2 +- .../admin/site_settings/registration/Registration.jsx | 2 +- app/javascript/components/home/AuthButtons.jsx | 2 +- app/javascript/components/rooms/room/join/JoinCard.jsx | 2 +- .../components/rooms/room/join/RequireAuthentication.jsx | 2 +- app/javascript/components/users/authentication/Signup.jsx | 2 +- esbuild.dev.mjs | 2 +- esbuild.mjs | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/controllers/api/v1/api_controller.rb b/app/controllers/api/v1/api_controller.rb index 502124b701..92b27351bf 100644 --- a/app/controllers/api/v1/api_controller.rb +++ b/app/controllers/api/v1/api_controller.rb @@ -89,7 +89,7 @@ def config_sorting(allowed_columns: []) { sort_column => sort_direction } end - # Checks if external authentication is enabled + # Checks if external authentication is enabled (currently only OIDC is implemented) def external_authn_enabled? ENV['OPENID_CONNECT_ISSUER'].present? end diff --git a/app/controllers/api/v1/env_controller.rb b/app/controllers/api/v1/env_controller.rb index 2541f5ed19..946e261c2f 100644 --- a/app/controllers/api/v1/env_controller.rb +++ b/app/controllers/api/v1/env_controller.rb @@ -25,7 +25,7 @@ class EnvController < ApiController # Returns basic NON-CONFIDENTIAL information on the environment variables def index render_data data: { - OPENID_CONNECT: ENV['OPENID_CONNECT_ISSUER'].present?, + EXTERNAL_AUTH: ENV['OPENID_CONNECT_ISSUER'].present?, # currently only OIDC is implemented HCAPTCHA_KEY: ENV.fetch('HCAPTCHA_SITE_KEY', nil), VERSION_TAG: ENV.fetch('VERSION_TAG', ''), CURRENT_PROVIDER: current_provider, diff --git a/app/javascript/components/admin/manage_users/ManageUsers.jsx b/app/javascript/components/admin/manage_users/ManageUsers.jsx index c72d9eef3b..b8269617b1 100644 --- a/app/javascript/components/admin/manage_users/ManageUsers.jsx +++ b/app/javascript/components/admin/manage_users/ManageUsers.jsx @@ -81,7 +81,7 @@ export default function ManageUsers() { /> )} { - (!envAPI.isLoading && !envAPI.data?.OPENID_CONNECT) + (!envAPI.isLoading && !envAPI.data?.EXTERNAL_AUTH) && ( - { env?.OPENID_CONNECT && ( + { env?.EXTERNAL_AUTH && ( diff --git a/app/javascript/components/rooms/room/join/JoinCard.jsx b/app/javascript/components/rooms/room/join/JoinCard.jsx index 41ac6ac491..73fc255feb 100644 --- a/app/javascript/components/rooms/room/join/JoinCard.jsx +++ b/app/javascript/components/rooms/room/join/JoinCard.jsx @@ -252,7 +252,7 @@ export default function JoinCard() { {!currentUser?.signed_in && ( - env?.OPENID_CONNECT ? ( + env?.EXTERNAL_AUTH ? ( {t('authentication.already_have_account')} diff --git a/app/javascript/components/rooms/room/join/RequireAuthentication.jsx b/app/javascript/components/rooms/room/join/RequireAuthentication.jsx index 98ed8676e0..88908ddcf0 100644 --- a/app/javascript/components/rooms/room/join/RequireAuthentication.jsx +++ b/app/javascript/components/rooms/room/join/RequireAuthentication.jsx @@ -38,7 +38,7 @@ export default function RequireAuthentication({ path }) { { - env?.OPENID_CONNECT ? ( + env?.EXTERNAL_AUTH ? (
diff --git a/app/javascript/components/users/authentication/Signup.jsx b/app/javascript/components/users/authentication/Signup.jsx index b5306175dc..7cbe8b3aa6 100644 --- a/app/javascript/components/users/authentication/Signup.jsx +++ b/app/javascript/components/users/authentication/Signup.jsx @@ -32,7 +32,7 @@ export default function Signup() { const envAPI = useEnv(); const isLoading = envAPI.isLoading || registrationMethodSettingAPI.isLoading; - if (envAPI.data?.OPENID_CONNECT) { + if (envAPI.data?.EXTERNAL_AUTH) { return ; } diff --git a/esbuild.dev.mjs b/esbuild.dev.mjs index 1bd3839ca4..3ff7311405 100644 --- a/esbuild.dev.mjs +++ b/esbuild.dev.mjs @@ -20,7 +20,7 @@ await esbuild.build({ }, define: { 'process.env.RELATIVE_URL_ROOT': `"${relativeUrlRoot}"`, - 'process.env.OMNIAUTH_PATH': `"${relativeUrlRoot}/auth/openid_connect"`, + 'process.env.OMNIAUTH_PATH': `"${relativeUrlRoot}/auth/openid_connect"`, // currently, only OIDC is implemented }, }); diff --git a/esbuild.mjs b/esbuild.mjs index 6330cbc21d..e9aa8a45e1 100644 --- a/esbuild.mjs +++ b/esbuild.mjs @@ -14,7 +14,7 @@ await esbuild.build({ }, define: { 'process.env.RELATIVE_URL_ROOT': `"${relativeUrlRoot}"`, - 'process.env.OMNIAUTH_PATH': `"${relativeUrlRoot}/auth/openid_connect"`, + 'process.env.OMNIAUTH_PATH': `"${relativeUrlRoot}/auth/openid_connect"`, // currently, only OIDC is implemented }, }); From ff806d9462e5b8c07272069f01db6232e59f25ce Mon Sep 17 00:00:00 2001 From: Ahmad Farhat Date: Wed, 1 Nov 2023 09:48:13 -0400 Subject: [PATCH 03/17] Make server recordings rake a bit smarter (#5484) * Make server recordings rake a bit smarter * Rubocop --- lib/tasks/server_recordings_sync.rake | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/tasks/server_recordings_sync.rake b/lib/tasks/server_recordings_sync.rake index c8621a3f3d..c74260742d 100644 --- a/lib/tasks/server_recordings_sync.rake +++ b/lib/tasks/server_recordings_sync.rake @@ -25,7 +25,15 @@ task :server_recordings_sync, %i[provider] => :environment do |_task, args| meeting_ids = rooms.pluck(:meeting_id) recordings = BigBlueButtonApi.new(provider: args[:provider]).get_recordings(meeting_ids:) + + # Skip the entire batch if the first and last recordings exist + if Recording.exists?(record_id: recordings[:recordings][0][:recordID]) && Recording.exists?(record_id: recordings[:recordings][-1][:recordID]) + next + end + recordings[:recordings].each do |recording| + next if Recording.exists?(record_id: recording[:recordID]) + RecordingCreator.new(recording:).call success 'Successfully migrated Recording:' info "RecordID: #{recording[:recordID]}" From ad747c1e13f79d04b14a1cbde33fb48d045feb26 Mon Sep 17 00:00:00 2001 From: Ahmad Farhat Date: Wed, 1 Nov 2023 09:48:25 -0400 Subject: [PATCH 04/17] Fix case sensitive emails (#5485) * case sensitive emails * Update schema.rb --- .../api/v1/admin/invitations_controller.rb | 2 +- .../api/v1/migrations/external_controller.rb | 2 +- .../api/v1/reset_password_controller.rb | 2 +- app/controllers/api/v1/sessions_controller.rb | 3 ++- app/controllers/api/v1/users_controller.rb | 3 ++- app/controllers/external_controller.rb | 2 +- app/services/recordings_sync.rb | 6 +++--- db/data/20231030185844_lowercase_emails.rb | 16 ++++++++++++++++ db/data_schema.rb | 2 +- db/schema.rb | 2 +- 10 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 db/data/20231030185844_lowercase_emails.rb diff --git a/app/controllers/api/v1/admin/invitations_controller.rb b/app/controllers/api/v1/admin/invitations_controller.rb index 5d022e8330..6aab95298c 100644 --- a/app/controllers/api/v1/admin/invitations_controller.rb +++ b/app/controllers/api/v1/admin/invitations_controller.rb @@ -39,7 +39,7 @@ def index # Creates an invitation for the specified emails (comma separated) and sends them an email def create params[:invitations][:emails].split(',').each do |email| - invitation = Invitation.find_or_initialize_by(email:, provider: current_provider).tap do |i| + invitation = Invitation.find_or_initialize_by(email: email.downcase, provider: current_provider).tap do |i| i.updated_at = Time.zone.now i.save! end diff --git a/app/controllers/api/v1/migrations/external_controller.rb b/app/controllers/api/v1/migrations/external_controller.rb index f9d39c1537..9a68ab20c8 100644 --- a/app/controllers/api/v1/migrations/external_controller.rb +++ b/app/controllers/api/v1/migrations/external_controller.rb @@ -91,7 +91,7 @@ def create_user return render_error(status: :bad_request, errors: 'Provider does not exist') end - return render_data status: :created if User.exists?(email: user_hash[:email], provider: user_hash[:provider]) + return render_data status: :created if User.exists?(email: user_hash[:email].downcase, provider: user_hash[:provider]) user_hash[:language] = I18n.default_locale if user_hash[:language].blank? || user_hash[:language] == 'default' diff --git a/app/controllers/api/v1/reset_password_controller.rb b/app/controllers/api/v1/reset_password_controller.rb index 99c7a8d30f..feea5ec0f0 100644 --- a/app/controllers/api/v1/reset_password_controller.rb +++ b/app/controllers/api/v1/reset_password_controller.rb @@ -30,7 +30,7 @@ def create # TODO: Log events. return render_error unless params[:user] - user = User.find_by email: params[:user][:email] + user = User.find_by email: params[:user][:email].downcase # Silently fail for unfound or external users. return render_data status: :ok unless user && !user.external_id? diff --git a/app/controllers/api/v1/sessions_controller.rb b/app/controllers/api/v1/sessions_controller.rb index d764ad97f0..65a07e9125 100644 --- a/app/controllers/api/v1/sessions_controller.rb +++ b/app/controllers/api/v1/sessions_controller.rb @@ -36,7 +36,8 @@ def create return render_error if hcaptcha_enabled? && !verify_hcaptcha(response: params[:token]) # Search for a user within the current provider and, if not found, search for a super admin within bn provider - user = User.find_by(email: session_params[:email], provider: current_provider) || User.find_by(email: session_params[:email], provider: 'bn') + user = User.find_by(email: session_params[:email].downcase, provider: current_provider) || + User.find_by(email: session_params[:email].downcase, provider: 'bn') # Return an error if the user is not found return render_error if user.blank? diff --git a/app/controllers/api/v1/users_controller.rb b/app/controllers/api/v1/users_controller.rb index 95b7a69e72..f00dd0e31d 100644 --- a/app/controllers/api/v1/users_controller.rb +++ b/app/controllers/api/v1/users_controller.rb @@ -169,7 +169,8 @@ def valid_invite_token return false if create_user_params[:invite_token].blank? # Try to delete the invitation and return true if it succeeds - Invitation.destroy_by(email: create_user_params[:email], provider: current_provider, token: create_user_params[:invite_token]).present? + Invitation.destroy_by(email: create_user_params[:email].downcase, provider: current_provider, + token: create_user_params[:invite_token]).present? end end end diff --git a/app/controllers/external_controller.rb b/app/controllers/external_controller.rb index b7f7a4d141..7e91680566 100644 --- a/app/controllers/external_controller.rb +++ b/app/controllers/external_controller.rb @@ -129,7 +129,7 @@ def valid_invite_token(email:) return false if token.blank? # Try to delete the invitation and return true if it succeeds - Invitation.destroy_by(email:, provider: current_provider, token:).present? + Invitation.destroy_by(email: email.downcase, provider: current_provider, token:).present? end def build_user_info(credentials) diff --git a/app/services/recordings_sync.rb b/app/services/recordings_sync.rb index af50e10bdf..bacdfd19fb 100644 --- a/app/services/recordings_sync.rb +++ b/app/services/recordings_sync.rb @@ -23,9 +23,9 @@ def initialize(room:, provider:) end def call - recordings = @room.recordings - Format.where(recordings:).delete_all - recordings.delete_all + room_recordings = @room.recordings + Format.where(recording: room_recordings).delete_all + room_recordings.delete_all recordings = BigBlueButtonApi.new(provider: @provider).get_recordings(meeting_ids: @room.meeting_id) recordings[:recordings].each do |recording| diff --git a/db/data/20231030185844_lowercase_emails.rb b/db/data/20231030185844_lowercase_emails.rb new file mode 100644 index 0000000000..fbe41a00bb --- /dev/null +++ b/db/data/20231030185844_lowercase_emails.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class LowercaseEmails < ActiveRecord::Migration[7.1] + def up + User.all.find_each(batch_size: 250) do |user| + downcase = user.email.downcase + next if user.email == downcase + + user.update(email: downcase) + end + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/data_schema.rb b/db/data_schema.rb index 4013193075..5e92e7bfec 100644 --- a/db/data_schema.rb +++ b/db/data_schema.rb @@ -1 +1 @@ -DataMigrate::Data.define(version: 20230228193705) +DataMigrate::Data.define(version: 20231030185844) diff --git a/db/schema.rb b/db/schema.rb index f0760fed4a..9cd0b8c663 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_07_05_183747) do +ActiveRecord::Schema[7.1].define(version: 2023_07_05_183747) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" From ae6ef9c3357da7b2dfbcf356e8406b1c31da550f Mon Sep 17 00:00:00 2001 From: Ahmad Farhat Date: Wed, 1 Nov 2023 10:01:57 -0400 Subject: [PATCH 05/17] Fixed various issues with users and providers (#5488) --- app/controllers/api/v1/admin/role_permissions_controller.rb | 5 ++++- app/controllers/api/v1/reset_password_controller.rb | 2 +- app/controllers/api/v1/shared_accesses_controller.rb | 1 + app/controllers/api/v1/verify_account_controller.rb | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/controllers/api/v1/admin/role_permissions_controller.rb b/app/controllers/api/v1/admin/role_permissions_controller.rb index 4b15af3767..b694807ca1 100644 --- a/app/controllers/api/v1/admin/role_permissions_controller.rb +++ b/app/controllers/api/v1/admin/role_permissions_controller.rb @@ -56,7 +56,10 @@ def role_params def create_default_room return unless role_params[:name] == 'CreateRoom' && role_params[:value] == true - User.includes(:rooms).where(role_id: role_params[:role_id]).where(rooms: { id: nil }).find_in_batches do |group| + User.includes(:rooms) + .with_provider(current_provider) + .where(role_id: role_params[:role_id]) + .where(rooms: { id: nil }).find_in_batches do |group| group.each do |user| Room.create(name: t('room.new_room_name', username: user.name, locale: user.language), user_id: user.id) end diff --git a/app/controllers/api/v1/reset_password_controller.rb b/app/controllers/api/v1/reset_password_controller.rb index feea5ec0f0..2384fd832d 100644 --- a/app/controllers/api/v1/reset_password_controller.rb +++ b/app/controllers/api/v1/reset_password_controller.rb @@ -30,7 +30,7 @@ def create # TODO: Log events. return render_error unless params[:user] - user = User.find_by email: params[:user][:email].downcase + user = User.find_by email: params[:user][:email].downcase, provider: current_provider # Silently fail for unfound or external users. return render_data status: :ok unless user && !user.external_id? diff --git a/app/controllers/api/v1/shared_accesses_controller.rb b/app/controllers/api/v1/shared_accesses_controller.rb index 864478dd1c..f8a3340a35 100644 --- a/app/controllers/api/v1/shared_accesses_controller.rb +++ b/app/controllers/api/v1/shared_accesses_controller.rb @@ -73,6 +73,7 @@ def shareable_users # Can't share the room if it's already shared or it's the room owner shareable_users = User.with_attached_avatar + .with_provider(current_provider) .where.not(id: [@room.shared_users.pluck(:id) << @room.user_id]) .where(role_id: [role_ids]) .name_search(params[:search]) diff --git a/app/controllers/api/v1/verify_account_controller.rb b/app/controllers/api/v1/verify_account_controller.rb index a0e4430ad5..8e508fc059 100644 --- a/app/controllers/api/v1/verify_account_controller.rb +++ b/app/controllers/api/v1/verify_account_controller.rb @@ -61,7 +61,7 @@ def activate def find_user_and_authorize return render_error status: :bad_request unless params[:user] - @user = User.find_by id: params[:user][:id] + @user = User.find_by id: params[:user][:id], provider: current_provider render_data status: :ok unless @user && !@user.verified? end end From 6486fee744384d6cb0975b969491c788a028929d Mon Sep 17 00:00:00 2001 From: Ahmad Farhat Date: Wed, 1 Nov 2023 11:31:08 -0400 Subject: [PATCH 06/17] Changes to local accounts (#5489) * Changes to local accounts * rspec * esf --- app/assets/locales/en.json | 2 ++ app/controllers/api/v1/api_controller.rb | 6 ++++-- app/controllers/api/v1/env_controller.rb | 2 +- app/controllers/api/v1/sessions_controller.rb | 6 ++++++ app/controllers/api/v1/users_controller.rb | 2 +- .../users/password_management/ResetPassword.jsx | 3 +++ .../hooks/mutations/sessions/useCreateSession.jsx | 2 ++ app/javascript/hooks/mutations/users/useResetPwd.jsx | 2 +- spec/controllers/users_controller_spec.rb | 12 ++++++------ 9 files changed, 26 insertions(+), 11 deletions(-) diff --git a/app/assets/locales/en.json b/app/assets/locales/en.json index 2fec3e4d1b..8d58b358fb 100644 --- a/app/assets/locales/en.json +++ b/app/assets/locales/en.json @@ -81,6 +81,7 @@ "account_info": "Account Info", "delete_account": "Delete Account", "change_password": "Change Password", + "set_password": "Set Your New Password", "reset_password": "Reset Password", "update_account_info": "Update Account Info", "current_password": "Current Password", @@ -358,6 +359,7 @@ "user_updated": "The user has been updated.", "user_deleted": "The user has been deleted.", "avatar_updated": "The avatar has been updated.", + "password_changed": "Successfully updated your password. Please sign in again.", "password_updated": "The password has been updated.", "account_activated": "Your account has been activated.", "activation_email_sent": "An email that contains the instructions to activate your account has been sent.", diff --git a/app/controllers/api/v1/api_controller.rb b/app/controllers/api/v1/api_controller.rb index 92b27351bf..0ebbe53fa1 100644 --- a/app/controllers/api/v1/api_controller.rb +++ b/app/controllers/api/v1/api_controller.rb @@ -90,8 +90,10 @@ def config_sorting(allowed_columns: []) end # Checks if external authentication is enabled (currently only OIDC is implemented) - def external_authn_enabled? - ENV['OPENID_CONNECT_ISSUER'].present? + def external_auth? + return ENV['OPENID_CONNECT_ISSUER'].present? if ENV['LOADBALANCER_ENDPOINT'].blank? + + !Tenant.exists?(name: current_provider, client_secret: 'local') end end end diff --git a/app/controllers/api/v1/env_controller.rb b/app/controllers/api/v1/env_controller.rb index 946e261c2f..4982b208e0 100644 --- a/app/controllers/api/v1/env_controller.rb +++ b/app/controllers/api/v1/env_controller.rb @@ -25,7 +25,7 @@ class EnvController < ApiController # Returns basic NON-CONFIDENTIAL information on the environment variables def index render_data data: { - EXTERNAL_AUTH: ENV['OPENID_CONNECT_ISSUER'].present?, # currently only OIDC is implemented + EXTERNAL_AUTH: external_auth?, HCAPTCHA_KEY: ENV.fetch('HCAPTCHA_SITE_KEY', nil), VERSION_TAG: ENV.fetch('VERSION_TAG', ''), CURRENT_PROVIDER: current_provider, diff --git a/app/controllers/api/v1/sessions_controller.rb b/app/controllers/api/v1/sessions_controller.rb index 65a07e9125..41eb19cbe6 100644 --- a/app/controllers/api/v1/sessions_controller.rb +++ b/app/controllers/api/v1/sessions_controller.rb @@ -45,6 +45,12 @@ def create # Will return an error if the user is NOT from the current provider and if the user is NOT a super admin return render_error if user.provider != current_provider && !user.super_admin? + # Password is not set (local user migrated from v2) + if user.external_id.blank? && user.password_digest.blank? + token = user.generate_reset_token! + return render_error data: token, errors: 'PasswordNotSet' + end + # TODO: Add proper error logging for non-verified token hcaptcha if user.authenticate(session_params[:password]) return render_error data: user.id, errors: Rails.configuration.custom_error_msgs[:unverified_user] unless user.verified? diff --git a/app/controllers/api/v1/users_controller.rb b/app/controllers/api/v1/users_controller.rb index f00dd0e31d..98b5202494 100644 --- a/app/controllers/api/v1/users_controller.rb +++ b/app/controllers/api/v1/users_controller.rb @@ -39,7 +39,7 @@ def show # POST /api/v1/users.json # Creates and saves a new user record in the database with the provided parameters def create - return render_error status: :forbidden if external_authn_enabled? + return render_error status: :forbidden if external_auth? # Check if this is an admin creating a user admin_create = current_user && PermissionsChecker.new(current_user:, permission_names: 'ManageUsers', current_provider:).call diff --git a/app/javascript/components/users/password_management/ResetPassword.jsx b/app/javascript/components/users/password_management/ResetPassword.jsx index b3ace1a9e2..56c35ab543 100644 --- a/app/javascript/components/users/password_management/ResetPassword.jsx +++ b/app/javascript/components/users/password_management/ResetPassword.jsx @@ -17,11 +17,13 @@ import React, { useEffect } from 'react'; import Card from 'react-bootstrap/Card'; import { useParams } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; import useVerifyToken from '../../../hooks/mutations/users/useVerifyToken'; import ResetPwdForm from './forms/ResetPwdForm'; import Logo from '../../shared_components/Logo'; export default function ResetPassword() { + const { t } = useTranslation(); const { token } = useParams(); const verifyTokenAPI = useVerifyToken(token); @@ -37,6 +39,7 @@ export default function ResetPassword() { + { t('user.account.set_password') } diff --git a/app/javascript/hooks/mutations/sessions/useCreateSession.jsx b/app/javascript/hooks/mutations/sessions/useCreateSession.jsx index 8018654916..10fef82ec0 100644 --- a/app/javascript/hooks/mutations/sessions/useCreateSession.jsx +++ b/app/javascript/hooks/mutations/sessions/useCreateSession.jsx @@ -49,6 +49,8 @@ export default function useCreateSession() { toast.error(t('toast.error.users.banned')); } else if (err.response.data.errors === 'UnverifiedUser') { navigate(`/verify?id=${err.response.data.data}`); + } else if (err.response.data.errors === 'PasswordNotSet') { + navigate(`/reset_password/${err.response.data.data}`); } else { toast.error(t('toast.error.session.invalid_credentials')); } diff --git a/app/javascript/hooks/mutations/users/useResetPwd.jsx b/app/javascript/hooks/mutations/users/useResetPwd.jsx index d2aae13349..8ff67d4684 100644 --- a/app/javascript/hooks/mutations/users/useResetPwd.jsx +++ b/app/javascript/hooks/mutations/users/useResetPwd.jsx @@ -28,7 +28,7 @@ export default function useResetPwd() { (user) => axios.post('/reset_password/reset.json', { user }), { onSuccess: () => { - toast.success(t('toast.success.user.password_updated')); + toast.success(t('toast.success.user.password_changed')); navigate('/signin'); }, onError: () => { diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 7c61036e82..e7f59cde50 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -25,7 +25,7 @@ before do ENV['SMTP_SERVER'] = 'test.com' - allow(controller).to receive(:external_authn_enabled?).and_return(false) + allow(controller).to receive(:external_auth?).and_return(false) request.headers['ACCEPT'] = 'application/json' end @@ -280,7 +280,7 @@ context 'External AuthN enabled' do before do - allow(controller).to receive(:external_authn_enabled?).and_return(true) + allow(controller).to receive(:external_auth?).and_return(true) end it 'returns :forbidden without creating the user account' do @@ -472,9 +472,9 @@ end context 'private methods' do - describe '#external_authn_enabled?' do + describe '#external_auth??' do before do - allow(controller).to receive(:external_authn_enabled?).and_call_original + allow(controller).to receive(:external_auth?).and_call_original end context 'OPENID_CONNECT_ISSUER is present?' do @@ -483,7 +483,7 @@ end it 'returns true' do - expect(controller).to be_external_authn_enabled + expect(controller).to be_external_auth end end @@ -493,7 +493,7 @@ end it 'returns false' do - expect(controller).not_to be_external_authn_enabled + expect(controller).not_to be_external_auth end end end From 63a86e314743b44f7329ec9eb68686517c6fdf91 Mon Sep 17 00:00:00 2001 From: Ahmad Farhat Date: Wed, 1 Nov 2023 11:57:41 -0400 Subject: [PATCH 07/17] Fix double logo on require authentication view (#5490) * Fix double logo on require authentication view * eslint --- .../rooms/room/join/RequireAuthentication.jsx | 52 ++++++++----------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/app/javascript/components/rooms/room/join/RequireAuthentication.jsx b/app/javascript/components/rooms/room/join/RequireAuthentication.jsx index 88908ddcf0..7e16a351aa 100644 --- a/app/javascript/components/rooms/room/join/RequireAuthentication.jsx +++ b/app/javascript/components/rooms/room/join/RequireAuthentication.jsx @@ -19,7 +19,6 @@ import { Button, Form } from 'react-bootstrap'; import Card from 'react-bootstrap/Card'; import { useTranslation } from 'react-i18next'; import PropTypes from 'prop-types'; -import Logo from '../../../shared_components/Logo'; import useEnv from '../../../../hooks/queries/env/useEnv'; import ButtonLink from '../../../shared_components/utilities/ButtonLink'; @@ -28,34 +27,29 @@ export default function RequireAuthentication({ path }) { const { data: env } = useEnv(); return ( -
-
- -
- - -

{ t('room.settings.require_signed_in_message') }

-
- - { - env?.EXTERNAL_AUTH ? ( - - - - - - ) : ( - <> - - {t('authentication.sign_up')} - - {t('authentication.sign_in')} - - ) - } - -
-
+ + +

{ t('room.settings.require_signed_in_message') }

+
+ + { + env?.EXTERNAL_AUTH ? ( +
+ + + +
+ ) : ( + <> + + {t('authentication.sign_up')} + + {t('authentication.sign_in')} + + ) + } +
+
); } From 1eb1917ef47ba4c1a3aa4dae23fac8143683d1fc Mon Sep 17 00:00:00 2001 From: Ahmad Farhat Date: Wed, 1 Nov 2023 14:46:57 -0400 Subject: [PATCH 08/17] More fixes for local migrations (#5491) * More fixes for local migrations * fix tests --- .../api/v1/migrations/external_controller.rb | 7 ++++- app/services/meeting_starter.rb | 2 +- .../admin/server_rooms_controller_spec.rb | 2 +- .../migrations/external_controller_spec.rb | 30 ++++++++++++++++--- spec/services/meeting_starter_spec.rb | 2 +- 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/app/controllers/api/v1/migrations/external_controller.rb b/app/controllers/api/v1/migrations/external_controller.rb index 9a68ab20c8..b398e241e5 100644 --- a/app/controllers/api/v1/migrations/external_controller.rb +++ b/app/controllers/api/v1/migrations/external_controller.rb @@ -105,6 +105,11 @@ def create_user return render_error(status: :bad_request, errors: user&.errors&.to_a) unless user.save + if user_hash[:provider] != 'greenlight' + user.password_digest = nil + user.save(validations: false) + end + render_data status: :created end @@ -250,7 +255,7 @@ def decrypted_params raise ActiveSupport::MessageEncryptor::InvalidMessage unless encrypted_params.is_a? String - crypt = ActiveSupport::MessageEncryptor.new(Rails.application.secrets.secret_key_base[0..31], cipher: 'aes-256-gcm', serializer: Marshal) + crypt = ActiveSupport::MessageEncryptor.new(Rails.application.secret_key_base[0..31], cipher: 'aes-256-gcm', serializer: Marshal) decrypted_params = crypt.decrypt_and_verify(encrypted_params) || {} raise ActiveSupport::MessageEncryptor::InvalidMessage unless decrypted_params.is_a? Hash diff --git a/app/services/meeting_starter.rb b/app/services/meeting_starter.rb index 4575aef490..4d9d62b84c 100644 --- a/app/services/meeting_starter.rb +++ b/app/services/meeting_starter.rb @@ -64,7 +64,7 @@ def computed_options(access_code:) logoutURL: room_url, meta_endCallbackUrl: meeting_ended_url(host: @base_url), 'meta_bbb-recording-ready-url': recording_ready_url(host: @base_url), - 'meta_bbb-origin-version': 3, + 'meta_bbb-origin-version': ENV.fetch('VERSION_TAG', 'v3'), 'meta_bbb-origin': 'greenlight' } end diff --git a/spec/controllers/admin/server_rooms_controller_spec.rb b/spec/controllers/admin/server_rooms_controller_spec.rb index b6d135b3be..10b2de3a77 100644 --- a/spec/controllers/admin/server_rooms_controller_spec.rb +++ b/spec/controllers/admin/server_rooms_controller_spec.rb @@ -154,7 +154,7 @@ def bbb_meetings hasJoinedVoice: 'false', hasVideo: 'false', clientType: 'HTML5' } }, - metadata: { 'bbb-origin-version': '3', + metadata: { 'bbb-origin-version': 'v3', 'bbb-recording-ready-url': 'http://localhost:3000/recording_ready', 'bbb-origin': 'greenlight', endcallbackurl: 'http://localhost:3000/meeting_ended' }, diff --git a/spec/controllers/migrations/external_controller_spec.rb b/spec/controllers/migrations/external_controller_spec.rb index e2cf4c2229..f15c602782 100644 --- a/spec/controllers/migrations/external_controller_spec.rb +++ b/spec/controllers/migrations/external_controller_spec.rb @@ -133,7 +133,7 @@ describe 'because the ciphertext was not generated with the same configuration' do it 'returns :bad_request without creating a role' do - key = Rails.application.secrets.secret_key_base[1..32] + key = Rails.application.secret_key_base[1..32] encrypted_params = encrypt_params({ role: { name: 'CrazyRole', role_permissions: {} } }, key:, expires_in: 10.seconds) expect { post :create_role, params: { v2: { encrypted_params: } } }.not_to change(Role, :count) @@ -188,6 +188,28 @@ expect(response).to have_http_status(:created) expect(user.password_digest).to be_present end + + it 'creates the user without a password if provider is not greenlight' do + tenant = create(:tenant) + role = create(:role, name: valid_user_role.name, provider: tenant.name) + valid_user_params[:provider] = tenant.name + + encrypted_params = encrypt_params({ user: valid_user_params }, expires_in: 10.seconds) + + expect_any_instance_of(described_class).to receive(:generate_secure_pwd).and_call_original + expect { post :create_user, params: { v2: { encrypted_params: } } }.to change(User, :count).from(0).to(1) + expect(ActionMailer::MailDeliveryJob).not_to have_been_enqueued + + user = User.take + expect(user.name).to eq(valid_user_params[:name]) + expect(user.email).to eq(valid_user_params[:email]) + expect(user.language).to eq(valid_user_params[:language]) + expect(user.role).to eq(role) + expect(user.session_token).to be_present + expect(user.provider).to eq(tenant.name) + expect(response).to have_http_status(:created) + expect(user.password_digest).not_to be_present + end end context 'when the provider does not exists' do @@ -429,7 +451,7 @@ describe 'because the ciphertext was not generated with the same configuration' do it 'returns :bad_request without creating a user' do - key = Rails.application.secrets.secret_key_base[1..32] + key = Rails.application.secret_key_base[1..32] encrypted_params = encrypt_params({ user: valid_user_params }, key:, expires_in: 10.seconds) expect_any_instance_of(described_class).not_to receive(:generate_secure_pwd) @@ -547,7 +569,7 @@ describe 'because the ciphertext was not generated with the same configuration' do it 'returns :bad_request without creating a room' do - key = Rails.application.secrets.secret_key_base[1..32] + key = Rails.application.secret_key_base[1..32] encrypted_params = encrypt_params({ room: valid_room_params }, key:, expires_in: 10.seconds) expect { post :create_room, params: { v2: { encrypted_params: } } }.not_to change(Room, :count) expect(response).to have_http_status(:bad_request) @@ -647,7 +669,7 @@ private def encrypt_params(params, key: nil, expires_at: nil, expires_in: nil, purpose: nil) - key = Rails.application.secrets.secret_key_base[0..31] if key.nil? + key = Rails.application.secret_key_base[0..31] if key.nil? crypt = ActiveSupport::MessageEncryptor.new(key, cipher: 'aes-256-gcm', serializer: Marshal) crypt.encrypt_and_sign(params, expires_at:, expires_in:, purpose:) end diff --git a/spec/services/meeting_starter_spec.rb b/spec/services/meeting_starter_spec.rb index 8877c3eb1e..275f606f64 100644 --- a/spec/services/meeting_starter_spec.rb +++ b/spec/services/meeting_starter_spec.rb @@ -41,7 +41,7 @@ logoutURL: url, meta_endCallbackUrl: File.join(base_url, '/meeting_ended'), 'meta_bbb-recording-ready-url': File.join(base_url, '/recording_ready'), - 'meta_bbb-origin-version': 3, + 'meta_bbb-origin-version': 'v3', 'meta_bbb-origin': 'greenlight', setting: 'value' } From 8a33bd8b6c5f57f75c27125e8fdf470368889d21 Mon Sep 17 00:00:00 2001 From: Ahmad Farhat Date: Wed, 1 Nov 2023 15:22:00 -0400 Subject: [PATCH 09/17] Hotfix for local accounts (#5492) --- app/services/user_creator.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/services/user_creator.rb b/app/services/user_creator.rb index 7a6ad286bc..79f22712bf 100644 --- a/app/services/user_creator.rb +++ b/app/services/user_creator.rb @@ -25,6 +25,7 @@ def initialize(user_params:, provider:, role:) end def call + @user_params[:email] = @user_params[:email].downcase email_role = infer_role_from_email(@user_params[:email]) User.new({ From 3a810f93e8cc37fa4c316e1c04a94b672dab38c9 Mon Sep 17 00:00:00 2001 From: Ahmad Farhat Date: Wed, 1 Nov 2023 17:17:45 -0400 Subject: [PATCH 10/17] Fix joining interval (#5494) --- app/javascript/components/rooms/room/join/JoinCard.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/components/rooms/room/join/JoinCard.jsx b/app/javascript/components/rooms/room/join/JoinCard.jsx index 73fc255feb..58521b86f5 100644 --- a/app/javascript/components/rooms/room/join/JoinCard.jsx +++ b/app/javascript/components/rooms/room/join/JoinCard.jsx @@ -77,7 +77,7 @@ export default function JoinCard() { } roomStatusAPI.mutate(data); - const interval = setInterval(() => roomStatusAPI.mutate(data), 5000); + const interval = setInterval(() => roomStatusAPI.mutate(data), 30000); setJoinInterval(interval); }; const reset = () => { setHasStarted(false); };// Reset pipeline; From f338b9be17fbec0dea99c2a5bd360db626ea5fb8 Mon Sep 17 00:00:00 2001 From: Ahmad Farhat Date: Fri, 3 Nov 2023 10:43:26 -0400 Subject: [PATCH 11/17] Recording view improvements (#5497) * Added pagination to recording tables * Make dropdown go up for last item in list * rspec --- .../stylesheets/application.bootstrap.scss | 4 +--- app/assets/stylesheets/helpers.scss | 6 +++--- app/assets/stylesheets/recordings.scss | 9 +++++++-- .../api/v1/recordings_controller.rb | 2 +- app/controllers/api/v1/rooms_controller.rb | 2 +- .../components/admin/manage_users/EditUser.jsx | 2 +- .../admin/manage_users/ManageUsers.jsx | 2 +- .../components/admin/roles/EditRole.jsx | 2 +- .../components/admin/roles/Roles.jsx | 2 +- .../admin/room_configuration/RoomConfig.jsx | 2 +- .../server_recordings/ServerRecordings.jsx | 2 +- .../admin/server_rooms/ServerRooms.jsx | 2 +- .../admin/site_settings/SiteSettings.jsx | 2 +- .../components/admin/tenants/Tenants.jsx | 2 +- .../components/recordings/RecordingRow.jsx | 5 ++++- .../components/recordings/RecordingsList.jsx | 18 +++++++++++++----- .../components/recordings/UserRecordings.jsx | 1 + .../room_recordings/RoomRecordings.jsx | 1 + .../room_recordings/RoomsRecordingRow.jsx | 5 ++++- .../utilities/SimpleSelect.jsx | 6 ++++-- spec/controllers/rooms_controller_spec.rb | 6 +++--- 21 files changed, 52 insertions(+), 31 deletions(-) diff --git a/app/assets/stylesheets/application.bootstrap.scss b/app/assets/stylesheets/application.bootstrap.scss index 2446bd5bf4..8339867fcf 100644 --- a/app/assets/stylesheets/application.bootstrap.scss +++ b/app/assets/stylesheets/application.bootstrap.scss @@ -197,8 +197,6 @@ input.search-bar { } #footer { - margin-top: $footer-buffer-height; - #footer-container { border-top: 1px solid #d0d5dd; } @@ -464,7 +462,7 @@ input.search-bar { box-shadow: 0 0 0 0.25rem var(--brand-color-light) !important; } &::after { - display: none; + display: none !important; } } diff --git a/app/assets/stylesheets/helpers.scss b/app/assets/stylesheets/helpers.scss index 107604e6ea..9a30d9c17e 100644 --- a/app/assets/stylesheets/helpers.scss +++ b/app/assets/stylesheets/helpers.scss @@ -34,14 +34,14 @@ } .no-header-height { - min-height: calc(100vh - $footer-height - $footer-buffer-height); + min-height: calc(100vh - $footer-height); } .regular-height { - min-height: calc(100vh - $header-height - $footer-height - $footer-buffer-height); + min-height: calc(100vh - $header-height - $footer-height); .vertical-center { - min-height: calc(100vh - $header-height - $header-height - $footer-height - $footer-buffer-height); + min-height: calc(100vh - $header-height - $header-height - $footer-height); } } diff --git a/app/assets/stylesheets/recordings.scss b/app/assets/stylesheets/recordings.scss index 9b770871cd..6d401795fb 100644 --- a/app/assets/stylesheets/recordings.scss +++ b/app/assets/stylesheets/recordings.scss @@ -13,10 +13,15 @@ // // You should have received a copy of the GNU Lesser General Public License along // with Greenlight; if not, see . +#user-recordings { + min-height: 699px; +} -#user-recordings, #room-recordings { - min-height: 400px; +#room-recordings { + min-height: 491px; +} +#user-recordings, #room-recordings { table { border-top-right-radius: $border-radius-lg; border-top-left-radius: $border-radius-lg; diff --git a/app/controllers/api/v1/recordings_controller.rb b/app/controllers/api/v1/recordings_controller.rb index 6138fafe38..09262909c0 100644 --- a/app/controllers/api/v1/recordings_controller.rb +++ b/app/controllers/api/v1/recordings_controller.rb @@ -37,7 +37,7 @@ class RecordingsController < ApiController def index sort_config = config_sorting(allowed_columns: %w[name length visibility]) - pagy, recordings = pagy(current_user.recordings&.order(sort_config, recorded_at: :desc)&.search(params[:search])) + pagy, recordings = pagy(current_user.recordings&.order(sort_config, recorded_at: :desc)&.search(params[:search]), items: 5) render_data data: recordings, meta: pagy_metadata(pagy), status: :ok end diff --git a/app/controllers/api/v1/rooms_controller.rb b/app/controllers/api/v1/rooms_controller.rb index ffde5ff5fd..e1e4a982e7 100644 --- a/app/controllers/api/v1/rooms_controller.rb +++ b/app/controllers/api/v1/rooms_controller.rb @@ -133,7 +133,7 @@ def purge_presentation def recordings sort_config = config_sorting(allowed_columns: %w[name length visibility]) - pagy, room_recordings = pagy(@room.recordings&.order(sort_config, recorded_at: :desc)&.search(params[:q])) + pagy, room_recordings = pagy(@room.recordings&.order(sort_config, recorded_at: :desc)&.search(params[:q]), items: 3) render_data data: room_recordings, meta: pagy_metadata(pagy), status: :ok end diff --git a/app/javascript/components/admin/manage_users/EditUser.jsx b/app/javascript/components/admin/manage_users/EditUser.jsx index 364c48c87e..fa85878b47 100644 --- a/app/javascript/components/admin/manage_users/EditUser.jsx +++ b/app/javascript/components/admin/manage_users/EditUser.jsx @@ -39,7 +39,7 @@ export default function EditUser() { } return ( -
+

{ t('admin.admin_panel') }

diff --git a/app/javascript/components/admin/manage_users/ManageUsers.jsx b/app/javascript/components/admin/manage_users/ManageUsers.jsx index b8269617b1..f256c9c47b 100644 --- a/app/javascript/components/admin/manage_users/ManageUsers.jsx +++ b/app/javascript/components/admin/manage_users/ManageUsers.jsx @@ -47,7 +47,7 @@ export default function ManageUsers() { } return ( -
+

{t('admin.admin_panel')}

diff --git a/app/javascript/components/admin/roles/EditRole.jsx b/app/javascript/components/admin/roles/EditRole.jsx index a546a7e9c9..883e765bf6 100644 --- a/app/javascript/components/admin/roles/EditRole.jsx +++ b/app/javascript/components/admin/roles/EditRole.jsx @@ -44,7 +44,7 @@ export default function EditRole() { if (isLoading) return null; return ( -
+

{ t('admin.admin_panel') }

diff --git a/app/javascript/components/admin/roles/Roles.jsx b/app/javascript/components/admin/roles/Roles.jsx index 78175a7087..d8ed915a7d 100644 --- a/app/javascript/components/admin/roles/Roles.jsx +++ b/app/javascript/components/admin/roles/Roles.jsx @@ -39,7 +39,7 @@ export default function Roles() { } return ( -
+

{ t('admin.admin_panel') }

diff --git a/app/javascript/components/admin/room_configuration/RoomConfig.jsx b/app/javascript/components/admin/room_configuration/RoomConfig.jsx index 3741539b66..696a5e1d91 100644 --- a/app/javascript/components/admin/room_configuration/RoomConfig.jsx +++ b/app/javascript/components/admin/room_configuration/RoomConfig.jsx @@ -37,7 +37,7 @@ export default function RoomConfig() { } return ( -
+

{ t('admin.admin_panel') }

diff --git a/app/javascript/components/admin/server_recordings/ServerRecordings.jsx b/app/javascript/components/admin/server_recordings/ServerRecordings.jsx index f51593b8ca..2b1ad378c5 100644 --- a/app/javascript/components/admin/server_recordings/ServerRecordings.jsx +++ b/app/javascript/components/admin/server_recordings/ServerRecordings.jsx @@ -38,7 +38,7 @@ export default function ServerRecordings() { } return ( -
+

{t('admin.admin_panel')}

diff --git a/app/javascript/components/admin/server_rooms/ServerRooms.jsx b/app/javascript/components/admin/server_rooms/ServerRooms.jsx index c0a69c1551..cde5c2e192 100644 --- a/app/javascript/components/admin/server_rooms/ServerRooms.jsx +++ b/app/javascript/components/admin/server_rooms/ServerRooms.jsx @@ -44,7 +44,7 @@ export default function ServerRooms() { } return ( -
+

{ t('admin.admin_panel') }

diff --git a/app/javascript/components/admin/site_settings/SiteSettings.jsx b/app/javascript/components/admin/site_settings/SiteSettings.jsx index f082194540..a8081e94ec 100644 --- a/app/javascript/components/admin/site_settings/SiteSettings.jsx +++ b/app/javascript/components/admin/site_settings/SiteSettings.jsx @@ -37,7 +37,7 @@ export default function SiteSettings() { } return ( -
+

{ t('admin.admin_panel') }

diff --git a/app/javascript/components/admin/tenants/Tenants.jsx b/app/javascript/components/admin/tenants/Tenants.jsx index 70a1630e0c..095239f172 100644 --- a/app/javascript/components/admin/tenants/Tenants.jsx +++ b/app/javascript/components/admin/tenants/Tenants.jsx @@ -45,7 +45,7 @@ export default function Tenants() { const { data: tenants, isLoading } = useTenants({ search: searchInput, page }); return ( -
+

{ t('admin.admin_panel') }

diff --git a/app/javascript/components/recordings/RecordingRow.jsx b/app/javascript/components/recordings/RecordingRow.jsx index a465a27ded..e6497890f0 100644 --- a/app/javascript/components/recordings/RecordingRow.jsx +++ b/app/javascript/components/recordings/RecordingRow.jsx @@ -35,7 +35,7 @@ import SimpleSelect from '../shared_components/utilities/SimpleSelect'; // TODO: Amir - Refactor this. export default function RecordingRow({ - recording, visibilityMutation: useVisibilityAPI, deleteMutation: useDeleteAPI, adminTable, + recording, visibilityMutation: useVisibilityAPI, deleteMutation: useDeleteAPI, adminTable, dropUp, }) { const { t } = useTranslation(); @@ -104,6 +104,7 @@ export default function RecordingRow({ {[...Array(recordingsProcessing)].map(() => )} { - (isLoading && [...Array(7)].map((val, idx) => ( + (isLoading && [...Array(numPlaceholders)].map((val, idx) => ( // eslint-disable-next-line react/no-array-index-key ))) } { - (recordings?.data?.length > 0 && recordings?.data?.map((recording) => ( - + (recordings?.data?.length > 0 && recordings?.data?.map((recording, idx) => ( + ))) } @@ -99,10 +104,11 @@ export default function RecordingsList({ } RecordingsList.defaultProps = { - recordings: { data: [], meta: { page: 1, pages: 1 } }, + recordings: { data: [], meta: { page: 1, pages: 1, items: 3 } }, recordingsProcessing: 0, searchInput: '', adminTable: false, + numPlaceholders: 7, }; RecordingsList.propTypes = { @@ -124,6 +130,7 @@ RecordingsList.propTypes = { meta: PropTypes.shape({ page: PropTypes.number, pages: PropTypes.number, + items: PropTypes.number, }), }), isLoading: PropTypes.bool.isRequired, @@ -132,4 +139,5 @@ RecordingsList.propTypes = { setSearchInput: PropTypes.func.isRequired, recordingsProcessing: PropTypes.number, adminTable: PropTypes.bool, + numPlaceholders: PropTypes.number, }; diff --git a/app/javascript/components/recordings/UserRecordings.jsx b/app/javascript/components/recordings/UserRecordings.jsx index ff8afc6579..cf43a52033 100644 --- a/app/javascript/components/recordings/UserRecordings.jsx +++ b/app/javascript/components/recordings/UserRecordings.jsx @@ -31,6 +31,7 @@ export default function UserRecordings() { setPage={setPage} setSearchInput={setSearchInput} searchInput={searchInput} + numPlaceholders={5} />
); diff --git a/app/javascript/components/recordings/room_recordings/RoomRecordings.jsx b/app/javascript/components/recordings/room_recordings/RoomRecordings.jsx index 687d2390e4..4de37a6934 100644 --- a/app/javascript/components/recordings/room_recordings/RoomRecordings.jsx +++ b/app/javascript/components/recordings/room_recordings/RoomRecordings.jsx @@ -36,6 +36,7 @@ export default function RoomRecordings() { setSearchInput={setSearchInput} searchInput={searchInput} recordingsProcessing={roomRecordingsProcessing.data} + numPlaceholders={3} />
); diff --git a/app/javascript/components/recordings/room_recordings/RoomsRecordingRow.jsx b/app/javascript/components/recordings/room_recordings/RoomsRecordingRow.jsx index 7a2b3d35a9..40ecc5e914 100644 --- a/app/javascript/components/recordings/room_recordings/RoomsRecordingRow.jsx +++ b/app/javascript/components/recordings/room_recordings/RoomsRecordingRow.jsx @@ -20,19 +20,21 @@ import useUpdateRecordingVisibility from '../../../hooks/mutations/recordings/us import useDeleteRecording from '../../../hooks/mutations/recordings/useDeleteRecording'; import RecordingRow from '../RecordingRow'; -export default function RoomsRecordingRow({ recording, adminTable }) { +export default function RoomsRecordingRow({ recording, adminTable, dropUp }) { return ( ); } RoomsRecordingRow.defaultProps = { adminTable: false, + dropUp: false, }; RoomsRecordingRow.propTypes = { @@ -51,4 +53,5 @@ RoomsRecordingRow.propTypes = { map: PropTypes.func, }).isRequired, adminTable: PropTypes.bool, + dropUp: PropTypes.bool, }; diff --git a/app/javascript/components/shared_components/utilities/SimpleSelect.jsx b/app/javascript/components/shared_components/utilities/SimpleSelect.jsx index 7ec44282de..6fd162fc97 100644 --- a/app/javascript/components/shared_components/utilities/SimpleSelect.jsx +++ b/app/javascript/components/shared_components/utilities/SimpleSelect.jsx @@ -19,12 +19,12 @@ import React from 'react'; import PropTypes from 'prop-types'; import { ChevronDownIcon } from '@heroicons/react/20/solid'; -export default function SimpleSelect({ defaultValue, children }) { +export default function SimpleSelect({ defaultValue, dropUp, children }) { // Get the currently selected option and set the dropdown toggle to that value const defaultString = children?.filter((item) => item.props.value === defaultValue)[0]; return ( - + { defaultString?.props?.children } @@ -38,10 +38,12 @@ export default function SimpleSelect({ defaultValue, children }) { SimpleSelect.defaultProps = { defaultValue: '', + dropUp: false, children: undefined, }; SimpleSelect.propTypes = { defaultValue: PropTypes.string, + dropUp: PropTypes.bool, children: PropTypes.arrayOf(PropTypes.element), }; diff --git a/spec/controllers/rooms_controller_spec.rb b/spec/controllers/rooms_controller_spec.rb index 469cbc0740..db67b94329 100644 --- a/spec/controllers/rooms_controller_spec.rb +++ b/spec/controllers/rooms_controller_spec.rb @@ -298,8 +298,8 @@ it 'returns recordings belonging to the room' do room1 = create(:room, user:, friendly_id: 'friendly_id_1') room2 = create(:room, user:, friendly_id: 'friendly_id_2') - recordings = create_list(:recording, 5, room: room1) - create_list(:recording, 5, room: room2) + recordings = create_list(:recording, 3, room: room1) + create_list(:recording, 3, room: room2) get :recordings, params: { friendly_id: room1.friendly_id } recording_ids = response.parsed_body['data'].pluck('id') expect(response).to have_http_status(:ok) @@ -320,7 +320,7 @@ create(:shared_access, user_id: shared_user.id, room_id: room.id) sign_in_user(shared_user) - recordings = create_list(:recording, 5, room:) + recordings = create_list(:recording, 3, room:) get :recordings, params: { friendly_id: room.friendly_id } From 139897fcfbdeaa0016e2e12abf00e1e89c676da8 Mon Sep 17 00:00:00 2001 From: Ahmad Farhat Date: Fri, 3 Nov 2023 11:33:47 -0400 Subject: [PATCH 12/17] Fix recordings processing to work with pagination (#5498) --- app/assets/locales/en.json | 1 + .../stylesheets/application.bootstrap.scss | 8 ++++ .../recordings/ProcessingRecordingRow.jsx | 42 ------------------- .../components/recordings/RecordingsList.jsx | 16 +++++-- 4 files changed, 22 insertions(+), 45 deletions(-) delete mode 100644 app/javascript/components/recordings/ProcessingRecordingRow.jsx diff --git a/app/assets/locales/en.json b/app/assets/locales/en.json index 8d58b358fb..48d9f9f4df 100644 --- a/app/assets/locales/en.json +++ b/app/assets/locales/en.json @@ -166,6 +166,7 @@ "recording": { "recording": "Recording", "recordings": "Recordings", + "processing": "Recordings Processing...", "name": "Name", "length": "Length", "users": "Users", diff --git a/app/assets/stylesheets/application.bootstrap.scss b/app/assets/stylesheets/application.bootstrap.scss index 8339867fcf..9254303d88 100644 --- a/app/assets/stylesheets/application.bootstrap.scss +++ b/app/assets/stylesheets/application.bootstrap.scss @@ -421,6 +421,14 @@ input.search-bar { } } +.badge-brand-outline { + font-size: 0.8rem; + border: 2px solid gainsboro; + color: var(--brand-color); + background-color: white !important; + box-shadow: var(--brand-color-light); +} + .setting-select { button { background: white !important; diff --git a/app/javascript/components/recordings/ProcessingRecordingRow.jsx b/app/javascript/components/recordings/ProcessingRecordingRow.jsx deleted file mode 100644 index 4414b744c0..0000000000 --- a/app/javascript/components/recordings/ProcessingRecordingRow.jsx +++ /dev/null @@ -1,42 +0,0 @@ -// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/. -// -// Copyright (c) 2022 BigBlueButton Inc. and by respective authors (see below). -// -// This program is free software; you can redistribute it and/or modify it under the -// terms of the GNU Lesser General Public License as published by the Free Software -// Foundation; either version 3.0 of the License, or (at your option) any later -// version. -// -// Greenlight is distributed in the hope that it will be useful, but WITHOUT ANY -// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A -// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along -// with Greenlight; if not, see . - -import { VideoCameraIcon } from '@heroicons/react/24/outline'; -import React from 'react'; -import { Stack } from 'react-bootstrap'; -import { useTranslation } from 'react-i18next'; - -export default function ProcessingRecordingRow() { - const { t } = useTranslation(); - - return ( - - - -
- -
- { t('recording.processing_recording') } -
- - - - - - - - ); -} diff --git a/app/javascript/components/recordings/RecordingsList.jsx b/app/javascript/components/recordings/RecordingsList.jsx index 0dde058ea4..c43e08658c 100644 --- a/app/javascript/components/recordings/RecordingsList.jsx +++ b/app/javascript/components/recordings/RecordingsList.jsx @@ -16,7 +16,9 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Card, Stack, Table } from 'react-bootstrap'; +import { + Badge, Card, Stack, Table, +} from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; import SortBy from '../shared_components/search/SortBy'; import RecordingsListRowPlaceHolder from './RecordingsListRowPlaceHolder'; @@ -25,7 +27,6 @@ import RoomsRecordingRow from './room_recordings/RoomsRecordingRow'; import Pagination from '../shared_components/Pagination'; import EmptyRecordingsList from './EmptyRecordingsList'; import SearchBar from '../shared_components/search/SearchBar'; -import ProcessingRecordingRow from './ProcessingRecordingRow'; export default function RecordingsList({ recordings, isLoading, setPage, searchInput, setSearchInput, recordingsProcessing, adminTable, numPlaceholders, @@ -42,6 +43,16 @@ export default function RecordingsList({
+ { recordingsProcessing > 0 && ( + + + + { recordingsProcessing } + + { t('recording.processing') } + + + )} { (searchInput && recordings?.data.length === 0) @@ -63,7 +74,6 @@ export default function RecordingsList({ - {[...Array(recordingsProcessing)].map(() => )} { (isLoading && [...Array(numPlaceholders)].map((val, idx) => ( // eslint-disable-next-line react/no-array-index-key From d32ef0476dd0d68cf16c0768e58e064d0c0ff74f Mon Sep 17 00:00:00 2001 From: Rahul Rodrigues Date: Mon, 13 Nov 2023 10:07:03 -0500 Subject: [PATCH 13/17] added delete presentation string to en.json and delete presentation form (#5510) --- app/assets/locales/en.json | 1 + .../rooms/room/presentation/forms/DeletePresentationForm.jsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/locales/en.json b/app/assets/locales/en.json index 48d9f9f4df..162b628ab7 100644 --- a/app/assets/locales/en.json +++ b/app/assets/locales/en.json @@ -130,6 +130,7 @@ "click_to_upload": "Click to Upload", "drag_and_drop": " or drag and drop", "upload_description": "Upload any office document or PDF file (not larger than {{size}}). Depending on the size of the file, it may require additional time to upload before it can be used", + "delete_presentation": "Delete Presentation", "are_you_sure_delete_presentation": "Are you sure you want to delete this presentation?" }, "shared_access": { diff --git a/app/javascript/components/rooms/room/presentation/forms/DeletePresentationForm.jsx b/app/javascript/components/rooms/room/presentation/forms/DeletePresentationForm.jsx index 0ea7414b8e..1646a1422c 100644 --- a/app/javascript/components/rooms/room/presentation/forms/DeletePresentationForm.jsx +++ b/app/javascript/components/rooms/room/presentation/forms/DeletePresentationForm.jsx @@ -37,7 +37,7 @@ export default function DeletePresentationForm({ handleClose }) { -

{ t('recording.delete_recording') }

+

{ t('room.presentation.delete_presentation') }

{ t('room.presentation.are_you_sure_delete_presentation') }

{ t('action_permanent') }

From 80ba1b4980e1d7c387fe87aef2b33495e93e536c Mon Sep 17 00:00:00 2001 From: Rahul Rodrigues Date: Tue, 14 Nov 2023 13:19:05 -0500 Subject: [PATCH 14/17] removed last 2 instances of recordings showing up when recordings disabled (#5518) * added delete presentation string to en.json and delete presentation form * removed last 2 instances of recordings that show up when recordings are disabled * only checking for sitewide recordings disable to remove the recording elements * removed unnecessary .jsx in import statement --- .../api/v1/rooms_configurations_controller.rb | 3 ++- app/javascript/components/home/HomePage.jsx | 18 +++++++++++------- .../components/rooms/room/join/JoinCard.jsx | 18 +++++++++++------- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/app/controllers/api/v1/rooms_configurations_controller.rb b/app/controllers/api/v1/rooms_configurations_controller.rb index 1b20935426..10266f5c9c 100644 --- a/app/controllers/api/v1/rooms_configurations_controller.rb +++ b/app/controllers/api/v1/rooms_configurations_controller.rb @@ -19,9 +19,10 @@ module Api module V1 class RoomsConfigurationsController < ApiController - before_action only: %i[index show] do + before_action only: %i[index] do ensure_authorized(%w[CreateRoom ManageSiteSettings ManageRoles ManageRooms], friendly_id: params[:friendly_id]) end + skip_before_action :ensure_authenticated, only: %i[show] # GET /api/v1/rooms_configurations.json # Fetches and returns all rooms configurations. diff --git a/app/javascript/components/home/HomePage.jsx b/app/javascript/components/home/HomePage.jsx index c131b589b2..2501bc5a74 100644 --- a/app/javascript/components/home/HomePage.jsx +++ b/app/javascript/components/home/HomePage.jsx @@ -26,6 +26,7 @@ import { import { toast } from 'react-toastify'; import { useAuth } from '../../contexts/auth/AuthProvider'; import HomepageFeatureCard from './HomepageFeatureCard'; +import useRoomConfigValue from '../../hooks/queries/rooms/useRoomConfigValue'; export default function HomePage() { const { t } = useTranslation(); @@ -33,6 +34,7 @@ export default function HomePage() { const navigate = useNavigate(); const [searchParams, setSearchParams] = useSearchParams(); const error = searchParams.get('error'); + const { data: recordValue } = useRoomConfigValue('record'); // Redirects the user to the proper page based on signed in status and CreateRoom permission useEffect( @@ -88,13 +90,15 @@ export default function HomePage() { icon={} /> - - } - /> - + { (recordValue !== 'false') && ( + + } + /> + + )} {publicRoom?.data.name} - - {t('view_recordings')} - + { (recordValue !== 'false') && ( + + {t('view_recordings')} + + )} From c4378b6fdad3d7a50539cb324a492c9a5cc1b1d8 Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Tue, 14 Nov 2023 13:34:55 -0500 Subject: [PATCH 15/17] Translate app/assets/locales/en.json in el (#5511) 100% translated source file: 'app/assets/locales/en.json' on 'el'. Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com> --- app/assets/locales/el.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/locales/el.json b/app/assets/locales/el.json index 2f4a8dda91..3590b585ba 100644 --- a/app/assets/locales/el.json +++ b/app/assets/locales/el.json @@ -81,6 +81,7 @@ "account_info": "Πληροφορίες λογαριασμού", "delete_account": "Διαγραφή λογαριασμού", "change_password": "Αλλαγή κωδικού πρόσβασης", + "set_password": "Ορίστε τον νέο κωδικό πρόσβασής σας", "reset_password": "Επαναφορά κωδικού πρόσβασης", "update_account_info": "Ενημέρωση πληροφοριών λογαριασμού", "current_password": "Τρέχων κωδικός πρόσβασης", @@ -129,6 +130,7 @@ "click_to_upload": "Κάντε κλικ για μεταφόρτωση ", "drag_and_drop": "ή σύρετε και αποθέστε το αρχείο", "upload_description": "Μεταφόρτωση εγγράφου ή αρχείου PDF (έως {{size}}). Ανάλογα με το μέγεθος του αρχείου, απαιτείται χρόνος για τη μεταφόρτωση πριν τη χρήση του", + "delete_presentation": "Διαγραφή παρουσίασης", "are_you_sure_delete_presentation": "Θέλετε σίγουρα να διαγράψετε αυτή την παρουσίαση;" }, "shared_access": { @@ -165,6 +167,7 @@ "recording": { "recording": "Καταγραφή", "recordings": "Καταγραφές", + "processing": "Επεξεργασία καταγραφών... ", "name": "Όνομα", "length": "Μήκος", "users": "Χρήστες", @@ -358,6 +361,7 @@ "user_updated": "Ο χρήστης ενημερώθηκε.", "user_deleted": "Ο χρήστης διαγράφηκε.", "avatar_updated": "Το άβαταρ ενημερώθηκε.", + "password_changed": "Επιτυχής ενημέρωση του κωδικού πρόσβασής σας. Παρακαλούμε συνδεθείτε ξανά.", "password_updated": "Ο κωδικός πρόσβασης ενημερώθηκε.", "account_activated": "Ο λογαριασμός ενεργοποιήθηκε επιτυχώς. Παρακαλούμε συνδεθείτε στον λογαριασμό σας.", "activation_email_sent": "Το email επιβεβαίωσης στάλθηκε.", From cc0b7300d2edad24d5dc24c8a770a479c1cec1cb Mon Sep 17 00:00:00 2001 From: Jesus Federico Date: Tue, 14 Nov 2023 15:07:45 -0500 Subject: [PATCH 16/17] [Snyk] Security upgrade active_storage_validations from 1.0.4 to 1.1.0 (#5481) * fix: Gemfile & Gemfile.lock to reduce vulnerabilities The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-RUBY-RACK-1061917 * Update Gemfile.lock --------- Co-authored-by: snyk-bot Co-authored-by: Ahmad Farhat --- Gemfile | 2 +- Gemfile.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 58d2376371..b467b484e5 100644 --- a/Gemfile +++ b/Gemfile @@ -6,7 +6,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } ruby '>= 3.0' gem 'active_model_serializers', '>= 0.10.14' -gem 'active_storage_validations', '>= 1.0.4' +gem 'active_storage_validations', '>= 1.1.0' gem 'aws-sdk-s3', require: false gem 'bcrypt', '~> 3.1.7' gem 'bigbluebutton-api-ruby', '1.9.1' diff --git a/Gemfile.lock b/Gemfile.lock index 29a3436b1c..35632e0404 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -54,7 +54,7 @@ GEM activemodel (>= 4.1) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) - active_storage_validations (1.0.4) + active_storage_validations (1.1.0) activejob (>= 5.2.0) activemodel (>= 5.2.0) activestorage (>= 5.2.0) @@ -257,7 +257,7 @@ GEM memoist (0.16.2) mini_magick (4.12.0) mini_mime (1.1.5) - mini_portile2 (2.8.4) + mini_portile2 (2.8.5) minitest (5.20.0) msgpack (1.6.0) multi_json (1.15.0) @@ -489,7 +489,7 @@ PLATFORMS DEPENDENCIES active_model_serializers (>= 0.10.14) - active_storage_validations (>= 1.0.4) + active_storage_validations (>= 1.1.0) aws-sdk-s3 bcrypt (~> 3.1.7) bigbluebutton-api-ruby (= 1.9.1) From 742def095e7a8b49a4663fe6d03c7e4103ba1b77 Mon Sep 17 00:00:00 2001 From: Ahmad Farhat Date: Tue, 14 Nov 2023 15:20:37 -0500 Subject: [PATCH 17/17] Fix failing rspecs (#5519) --- app/controllers/health_checks_controller.rb | 2 +- spec/models/site_setting_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/health_checks_controller.rb b/app/controllers/health_checks_controller.rb index 465e3bc209..970f6c0e1b 100644 --- a/app/controllers/health_checks_controller.rb +++ b/app/controllers/health_checks_controller.rb @@ -39,7 +39,7 @@ def check def check_database raise 'Unable to connect to Database' unless ActiveRecord::Base.connection.active? - raise 'Unable to connect to Database - pending migrations' unless ActiveRecord::Migration.check_pending!.nil? + raise 'Unable to connect to Database - pending migrations' unless ActiveRecord::Migration.check_all_pending!.nil? rescue StandardError => e raise "Unable to connect to Database - #{e}" end diff --git a/spec/models/site_setting_spec.rb b/spec/models/site_setting_spec.rb index 0328dc8b8f..f9b1806943 100644 --- a/spec/models/site_setting_spec.rb +++ b/spec/models/site_setting_spec.rb @@ -48,7 +48,7 @@ it 'fails if the attachement is too large' do site_setting = build(:site_setting, image: fixture_file_upload(file_fixture('large-avatar.jpg'), 'image/jpeg')) expect(site_setting).to be_invalid - expect(site_setting.errors).to be_of_kind(:image, :file_size_out_of_range) + expect(site_setting.errors).to be_of_kind(:image, :file_size_not_less_than) end end end