diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 86bfec749b..0000000000 --- a/Gemfile.lock +++ /dev/null @@ -1,553 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - actioncable (7.1.3.4) - actionpack (= 7.1.3.4) - activesupport (= 7.1.3.4) - nio4r (~> 2.0) - websocket-driver (>= 0.6.1) - zeitwerk (~> 2.6) - actionmailbox (7.1.3.4) - actionpack (= 7.1.3.4) - activejob (= 7.1.3.4) - activerecord (= 7.1.3.4) - activestorage (= 7.1.3.4) - activesupport (= 7.1.3.4) - mail (>= 2.7.1) - net-imap - net-pop - net-smtp - actionmailer (7.1.3.4) - actionpack (= 7.1.3.4) - actionview (= 7.1.3.4) - activejob (= 7.1.3.4) - activesupport (= 7.1.3.4) - mail (~> 2.5, >= 2.5.4) - net-imap - net-pop - net-smtp - rails-dom-testing (~> 2.2) - actionpack (7.1.3.4) - actionview (= 7.1.3.4) - activesupport (= 7.1.3.4) - nokogiri (>= 1.8.5) - racc - rack (>= 2.2.4) - rack-session (>= 1.0.1) - rack-test (>= 0.6.3) - rails-dom-testing (~> 2.2) - rails-html-sanitizer (~> 1.6) - actiontext (7.1.3.4) - actionpack (= 7.1.3.4) - activerecord (= 7.1.3.4) - activestorage (= 7.1.3.4) - activesupport (= 7.1.3.4) - globalid (>= 0.6.0) - nokogiri (>= 1.8.5) - actionview (7.1.3.4) - activesupport (= 7.1.3.4) - builder (~> 3.1) - erubi (~> 1.11) - rails-dom-testing (~> 2.2) - rails-html-sanitizer (~> 1.6) - active_model_serializers (0.10.14) - actionpack (>= 4.1) - activemodel (>= 4.1) - case_transform (>= 0.2) - jsonapi-renderer (>= 0.1.1.beta1, < 0.3) - active_storage_validations (1.1.0) - activejob (>= 5.2.0) - activemodel (>= 5.2.0) - activestorage (>= 5.2.0) - activesupport (>= 5.2.0) - activejob (7.1.3.4) - activesupport (= 7.1.3.4) - globalid (>= 0.3.6) - activemodel (7.1.3.4) - activesupport (= 7.1.3.4) - activerecord (7.1.3.4) - activemodel (= 7.1.3.4) - activesupport (= 7.1.3.4) - timeout (>= 0.4.0) - activestorage (7.1.3.4) - actionpack (= 7.1.3.4) - activejob (= 7.1.3.4) - activerecord (= 7.1.3.4) - activesupport (= 7.1.3.4) - marcel (~> 1.0) - activesupport (7.1.3.4) - base64 - bigdecimal - concurrent-ruby (~> 1.0, >= 1.0.2) - connection_pool (>= 2.2.5) - drb - i18n (>= 1.6, < 2) - minitest (>= 5.1) - mutex_m - tzinfo (~> 2.0) - addressable (2.8.1) - public_suffix (>= 2.0.2, < 6.0) - aes_key_wrap (1.1.0) - ast (2.4.2) - attr_required (1.0.1) - aws-eventstream (1.2.0) - aws-partitions (1.708.0) - aws-sdk-core (3.170.0) - aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.5) - jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.62.0) - aws-sdk-core (~> 3, >= 3.165.0) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.119.0) - aws-sdk-core (~> 3, >= 3.165.0) - aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.4) - aws-sigv4 (1.5.2) - aws-eventstream (~> 1, >= 1.0.2) - base64 (0.1.1) - bcrypt (3.1.18) - bigbluebutton-api-ruby (1.9.1) - childprocess (>= 1.0.1) - ffi (>= 1.9.24) - json (>= 1.8.6) - nokogiri (>= 1.10.4) - rack (>= 1.6.11) - rubyzip (>= 1.3.0) - xml-simple (~> 1.1) - bigdecimal (3.1.8) - bindata (2.5.0) - bindex (0.8.1) - bootsnap (1.16.0) - msgpack (~> 1.2) - builder (3.3.0) - capybara (3.38.0) - addressable - matrix - mini_mime (>= 0.1.3) - nokogiri (~> 1.8) - rack (>= 1.6.0) - rack-test (>= 0.6.3) - regexp_parser (>= 1.5, < 3.0) - xpath (~> 3.2) - case_transform (0.2) - activesupport - childprocess (4.1.0) - clamby (1.6.10) - concurrent-ruby (1.3.3) - connection_pool (2.4.1) - crack (0.4.5) - rexml - crass (1.0.6) - cssbundling-rails (1.3.3) - railties (>= 6.0.0) - data_migrate (9.4.0) - activerecord (>= 6.1) - railties (>= 6.1) - date (3.3.4) - debug (1.7.1) - irb (>= 1.5.0) - reline (>= 0.3.1) - declarative (0.0.20) - diff-lcs (1.5.0) - digest-crc (0.6.5) - rake (>= 12.0.0, < 14.0.0) - dotenv (2.8.1) - dotenv-rails (2.8.1) - dotenv (= 2.8.1) - railties (>= 3.2) - drb (2.2.1) - erubi (1.12.0) - factory_bot (6.4.5) - activesupport (>= 5.0.0) - factory_bot_rails (6.4.3) - factory_bot (~> 6.4) - railties (>= 5.0.0) - faker (3.1.1) - i18n (>= 1.8.11, < 2) - faraday (2.9.0) - faraday-net_http (>= 2.0, < 3.2) - faraday-follow_redirects (0.3.0) - faraday (>= 1, < 3) - faraday-net_http (3.1.0) - net-http - ffi (1.15.5) - globalid (1.2.1) - activesupport (>= 6.1) - google-apis-core (0.11.0) - addressable (~> 2.5, >= 2.5.1) - googleauth (>= 0.16.2, < 2.a) - httpclient (>= 2.8.1, < 3.a) - mini_mime (~> 1.0) - representable (~> 3.0) - retriable (>= 2.0, < 4.a) - rexml - webrick - google-apis-iamcredentials_v1 (0.17.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-storage_v1 (0.19.0) - google-apis-core (>= 0.9.0, < 2.a) - google-cloud-core (1.6.0) - google-cloud-env (~> 1.0) - google-cloud-errors (~> 1.0) - google-cloud-env (1.6.0) - faraday (>= 0.17.3, < 3.0) - google-cloud-errors (1.3.1) - google-cloud-storage (1.44.0) - addressable (~> 2.8) - digest-crc (~> 0.4) - google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.19.0) - google-cloud-core (~> 1.6) - googleauth (>= 0.16.2, < 2.a) - mini_mime (~> 1.0) - googleauth (1.6.0) - faraday (>= 0.17.3, < 3.a) - jwt (>= 1.4, < 3.0) - memoist (~> 0.16) - multi_json (~> 1.11) - os (>= 0.9, < 2.0) - signet (>= 0.16, < 2.a) - hashdiff (1.0.1) - hashie (5.0.0) - hcaptcha (7.1.0) - json - hiredis (0.6.3) - httpclient (2.8.3) - i18n (1.14.5) - concurrent-ruby (~> 1.0) - i18n-language-mapping (0.1.3.1) - image_processing (1.12.2) - mini_magick (>= 4.9.5, < 5) - ruby-vips (>= 2.0.17, < 3) - io-console (0.7.2) - irb (1.13.1) - rdoc (>= 4.0.0) - reline (>= 0.4.2) - jbuilder (2.11.5) - actionview (>= 5.0.0) - activesupport (>= 5.0.0) - jmespath (1.6.2) - jsbundling-rails (1.2.2) - railties (>= 6.0.0) - json (2.6.3) - json-jwt (1.16.6) - activesupport (>= 4.2) - aes_key_wrap - base64 - bindata - faraday (~> 2.0) - faraday-follow_redirects - jsonapi-renderer (0.2.2) - jwt (2.7.0) - language_server-protocol (3.17.0.3) - lograge (0.14.0) - actionpack (>= 4) - activesupport (>= 4) - railties (>= 4) - request_store (~> 1.0) - loofah (2.22.0) - crass (~> 1.0.2) - nokogiri (>= 1.12.0) - mail (2.8.1) - mini_mime (>= 0.1.1) - net-imap - net-pop - net-smtp - marcel (1.0.4) - matrix (0.4.2) - memoist (0.16.2) - mini_magick (4.12.0) - mini_mime (1.1.5) - mini_portile2 (2.8.7) - minitest (5.23.1) - msgpack (1.6.0) - multi_json (1.15.0) - mutex_m (0.2.0) - net-http (0.4.1) - uri - net-imap (0.4.12) - date - net-protocol - net-pop (0.1.2) - net-protocol - net-protocol (0.2.2) - timeout - net-smtp (0.5.0) - net-protocol - nio4r (2.7.3) - nokogiri (1.16.5) - mini_portile2 (~> 2.8.2) - racc (~> 1.4) - nokogiri (1.16.5-x86_64-linux) - racc (~> 1.4) - omniauth (2.1.2) - hashie (>= 3.4.6) - rack (>= 2.2.3) - rack-protection - omniauth-rails_csrf_protection (1.0.2) - actionpack (>= 4.2) - omniauth (~> 2.0) - omniauth_openid_connect (0.7.1) - omniauth (>= 1.9, < 3) - openid_connect (~> 2.2) - openid_connect (2.2.0) - activemodel - attr_required (>= 1.0.0) - faraday (~> 2.0) - faraday-follow_redirects - json-jwt (>= 1.16) - net-smtp - rack-oauth2 (~> 2.2) - swd (~> 2.0) - tzinfo - validate_email - validate_url - webfinger (~> 2.0) - os (1.1.4) - pagy (6.0.4) - parallel (1.23.0) - parser (3.2.2.3) - ast (~> 2.4.1) - racc - pg (1.4.5) - psych (5.1.2) - stringio - public_suffix (5.0.3) - puma (5.6.8) - nio4r (~> 2.0) - racc (1.8.0) - rack (2.2.9) - rack-oauth2 (2.2.0) - activesupport - attr_required - faraday (~> 2.0) - faraday-follow_redirects - json-jwt (>= 1.11.0) - rack (>= 2.1.0) - rack-protection (3.2.0) - base64 (>= 0.1.0) - rack (~> 2.2, >= 2.2.4) - rack-session (1.0.2) - rack (< 3) - rack-test (2.1.0) - rack (>= 1.3) - rackup (1.0.0) - rack (< 3) - webrick - rails (7.1.3.4) - actioncable (= 7.1.3.4) - actionmailbox (= 7.1.3.4) - actionmailer (= 7.1.3.4) - actionpack (= 7.1.3.4) - actiontext (= 7.1.3.4) - actionview (= 7.1.3.4) - activejob (= 7.1.3.4) - activemodel (= 7.1.3.4) - activerecord (= 7.1.3.4) - activestorage (= 7.1.3.4) - activesupport (= 7.1.3.4) - bundler (>= 1.15.0) - railties (= 7.1.3.4) - rails-dom-testing (2.2.0) - activesupport (>= 5.0.0) - minitest - nokogiri (>= 1.6) - rails-html-sanitizer (1.6.0) - loofah (~> 2.21) - nokogiri (~> 1.14) - railties (7.1.3.4) - actionpack (= 7.1.3.4) - activesupport (= 7.1.3.4) - irb - rackup (>= 1.0.0) - rake (>= 12.2) - thor (~> 1.0, >= 1.2.2) - zeitwerk (~> 2.6) - rainbow (3.1.1) - rake (13.2.1) - rdoc (6.7.0) - psych (>= 4.0.0) - redis (4.8.0) - regexp_parser (2.8.1) - reline (0.5.9) - io-console (~> 0.5) - remote_syslog_logger (1.0.4) - syslog_protocol - representable (3.2.0) - declarative (< 0.1.0) - trailblazer-option (>= 0.1.1, < 0.2.0) - uber (< 0.2.0) - request_store (1.5.1) - rack (>= 1.4) - retriable (3.1.2) - rexml (3.2.6) - rspec-core (3.12.2) - rspec-support (~> 3.12.0) - rspec-expectations (3.12.3) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-mocks (3.12.6) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-rails (6.1.0) - actionpack (>= 6.1) - activesupport (>= 6.1) - railties (>= 6.1) - rspec-core (~> 3.12) - rspec-expectations (~> 3.12) - rspec-mocks (~> 3.12) - rspec-support (~> 3.12) - rspec-support (3.12.1) - rubocop (1.56.1) - base64 (~> 0.1.1) - json (~> 2.3) - language_server-protocol (>= 3.17.0) - parallel (~> 1.10) - parser (>= 3.2.2.3) - rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.28.1, < 2.0) - ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.29.0) - parser (>= 3.2.1.0) - rubocop-capybara (2.19.0) - rubocop (~> 1.41) - rubocop-factory_bot (2.24.0) - rubocop (~> 1.33) - rubocop-performance (1.16.0) - rubocop (>= 1.7.0, < 2.0) - rubocop-ast (>= 0.4.0) - rubocop-rails (2.20.2) - activesupport (>= 4.2.0) - rack (>= 1.1) - rubocop (>= 1.33.0, < 2.0) - rubocop-rspec (2.9.0) - rubocop (~> 1.19) - ruby-progressbar (1.13.0) - ruby-vips (2.1.4) - ffi (~> 1.12) - rubyzip (2.3.2) - selenium-webdriver (4.8.0) - rexml (~> 3.2, >= 3.2.5) - rubyzip (>= 1.2.2, < 3.0) - websocket (~> 1.0) - shoulda-matchers (5.3.0) - activesupport (>= 5.2.0) - signet (0.17.0) - addressable (~> 2.8) - faraday (>= 0.17.5, < 3.a) - jwt (>= 1.5, < 3.0) - multi_json (~> 1.10) - sprockets (4.2.1) - concurrent-ruby (~> 1.0) - rack (>= 2.2.4, < 4) - sprockets-rails (3.5.0) - actionpack (>= 6.1) - activesupport (>= 6.1) - sprockets (>= 3.0.0) - stringio (3.1.0) - swd (2.0.2) - activesupport (>= 3) - attr_required (>= 0.0.5) - faraday (~> 2.0) - faraday-follow_redirects - syslog_protocol (0.9.2) - thor (1.3.1) - timeout (0.4.1) - trailblazer-option (0.1.2) - tzinfo (2.0.6) - concurrent-ruby (~> 1.0) - uber (0.1.0) - unicode-display_width (2.4.2) - uri (0.13.0) - validate_email (0.1.6) - activemodel (>= 3.0) - mail (>= 2.2.5) - validate_url (1.0.15) - activemodel (>= 3.0.0) - public_suffix - web-console (4.2.1) - actionview (>= 6.0.0) - activemodel (>= 6.0.0) - bindex (>= 0.4.0) - railties (>= 6.0.0) - webdrivers (5.2.0) - nokogiri (~> 1.6) - rubyzip (>= 1.3.0) - selenium-webdriver (~> 4.0) - webfinger (2.1.2) - activesupport - faraday (~> 2.0) - faraday-follow_redirects - webmock (3.18.1) - addressable (>= 2.8.0) - crack (>= 0.3.2) - hashdiff (>= 0.4.0, < 2.0.0) - webrick (1.8.1) - websocket (1.2.9) - websocket-driver (0.7.6) - websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.5) - xml-simple (1.1.9) - rexml - xpath (3.2.0) - nokogiri (~> 1.8) - zeitwerk (2.6.15) - -PLATFORMS - ruby - x86_64-linux - -DEPENDENCIES - active_model_serializers (>= 0.10.14) - active_storage_validations (>= 1.1.0) - aws-sdk-s3 - bcrypt (~> 3.1.7) - bigbluebutton-api-ruby (= 1.9.1) - bootsnap - capybara - clamby (~> 1.6.10) - cssbundling-rails (>= 1.3.3) - data_migrate (>= 9.4.0) - debug - dotenv-rails - factory_bot (>= 6.4.1) - factory_bot_rails (>= 6.4.3) - faker - google-cloud-storage (~> 1.44) - hcaptcha - hiredis (~> 0.6.0) - i18n-language-mapping - image_processing (~> 1.2) - jbuilder - jsbundling-rails (>= 1.2.2) - jwt - lograge (~> 0.14.0) - mini_magick (>= 4.9.5) - omniauth (~> 2.1.2) - omniauth-rails_csrf_protection (~> 1.0.2) - omniauth_openid_connect (>= 0.6.1) - pagy (~> 6.0, >= 6.0.0) - pg - puma (~> 5.6) - rails (~> 7.1.3, >= 7.1.3.3) - redis (~> 4.0) - remote_syslog_logger - rspec-rails (>= 6.0.4) - rubocop (~> 1.26) - rubocop-capybara (~> 2.19.0) - rubocop-factory_bot (~> 2.24.0) - rubocop-performance (~> 1.13) - rubocop-rails (~> 2.18, >= 2.18.0) - rubocop-rspec (~> 2.9.0) - selenium-webdriver - shoulda-matchers (~> 5.0) - sprockets-rails (>= 3.5.0) - tzinfo-data - web-console (>= 4.2.1) - webdrivers - webmock diff --git a/app/assets/locales/en.json b/app/assets/locales/en.json index d3ddd9c7ab..9fd1a2afba 100644 --- a/app/assets/locales/en.json +++ b/app/assets/locales/en.json @@ -319,7 +319,10 @@ "open": "Open Registration", "invite": "Join by Invitation", "approval": "Approve/Decline" - } + }, + "allowed_domains": "Allow Specific Email Domains", + "allowed_domains_signup_description": "Allow specific email domains to sign up. Format must be: @test.com,domain.com", + "enter_allowed_domains_rule" : "Enter the allowed domains" } }, "room_configuration": { @@ -420,7 +423,8 @@ "privacy_policy_updated": "The privacy notice has been updated.", "helpcenter_updated": "The help center link has been updated.", "terms_of_service_updated": "The terms of service have been updated.", - "maintenance_updated": "The maintenance banner has been updated." + "maintenance_updated": "The maintenance banner has been updated.", + "allowed_domains_signup_updated": "The specific email domain sign up has been updated" }, "recording": { "recording_visibility_updated": "The recording visibility has been updated.", diff --git a/app/controllers/api/v1/users_controller.rb b/app/controllers/api/v1/users_controller.rb index d28c0834a3..6661d71a91 100644 --- a/app/controllers/api/v1/users_controller.rb +++ b/app/controllers/api/v1/users_controller.rb @@ -61,6 +61,9 @@ def create # Users created by a user will have the creator language by default with a fallback to the server configured default_locale. create_user_params[:language] = current_user&.language || I18n.default_locale if create_user_params[:language].blank? + # renders an error if the user is signing up with an invalid domain based off site settings + return render_error errors: Rails.configuration.custom_error_msgs[:unauthorized], status: :forbidden unless valid_domain? + user = UserCreator.new(user_params: create_user_params.except(:invite_token), provider: current_provider, role: default_role).call smtp_enabled = ENV['SMTP_SERVER'].present? @@ -184,6 +187,17 @@ def valid_invite_token Invitation.destroy_by(email: create_user_params[:email].downcase, provider: current_provider, token: create_user_params[:invite_token]).present? end + + def valid_domain? + allowed_domains_emails = SettingGetter.new(setting_name: 'AllowedDomains', provider: current_provider).call + return true if allowed_domains_emails.blank? + + domains = allowed_domains_emails.split(',') + domains.each do |domain| + return true if create_user_params[:email].end_with?(domain) + end + false + end end end end diff --git a/app/controllers/external_controller.rb b/app/controllers/external_controller.rb index 085805aa19..ec57eb1006 100644 --- a/app/controllers/external_controller.rb +++ b/app/controllers/external_controller.rb @@ -41,6 +41,8 @@ def create_user return redirect_to root_path(error: Rails.configuration.custom_error_msgs[:invite_token_invalid]) end + return render_error status: :forbidden unless valid_domain?(user_info[:email]) + # Create the user if they dont exist if new_user user = UserCreator.new(user_params: user_info, provider: current_provider, role: default_role).call @@ -164,4 +166,15 @@ def build_user_info(credentials) verified: true } end + + def valid_domain?(email) + allowed_domain_emails = SettingGetter.new(setting_name: 'AllowedDomains', provider: current_provider).call + return true if allowed_domain_emails.blank? + + domains = allowed_domain_emails.split(',') + domains.each do |domain| + return true if email.end_with?(domain) + end + false + end end diff --git a/app/javascript/components/admin/site_settings/registration/Registration.jsx b/app/javascript/components/admin/site_settings/registration/Registration.jsx index 6b75f888bc..7d9029666f 100644 --- a/app/javascript/components/admin/site_settings/registration/Registration.jsx +++ b/app/javascript/components/admin/site_settings/registration/Registration.jsx @@ -28,11 +28,12 @@ import useRoles from '../../../../hooks/queries/admin/roles/useRoles'; export default function Registration() { const { t } = useTranslation(); const { data: env } = useEnv(); - const { data: siteSettings } = useSiteSettings(['RoleMapping', 'DefaultRole', 'ResyncOnLogin', 'RegistrationMethod']); + const { data: siteSettings } = useSiteSettings(['RoleMapping', 'DefaultRole', 'ResyncOnLogin', 'RegistrationMethod', 'AllowedDomains']); const { data: roles } = useRoles(); const updateRegistrationMethod = useUpdateSiteSetting('RegistrationMethod'); const updateDefaultRole = useUpdateSiteSetting('DefaultRole'); const updateRoleMapping = useUpdateSiteSetting('RoleMapping'); + const updateDomainSignUp = useUpdateSiteSetting('AllowedDomains'); return ( <> @@ -99,6 +100,24 @@ export default function Registration() { + + + {t('admin.site_settings.registration.allowed_domains')} +

{t('admin.site_settings.registration.allowed_domains_signup_description')}

+ + + + +
); } diff --git a/app/javascript/hooks/mutations/admin/site_settings/useUpdateSiteSetting.jsx b/app/javascript/hooks/mutations/admin/site_settings/useUpdateSiteSetting.jsx index 9b3725952f..7e4f4e3c0c 100644 --- a/app/javascript/hooks/mutations/admin/site_settings/useUpdateSiteSetting.jsx +++ b/app/javascript/hooks/mutations/admin/site_settings/useUpdateSiteSetting.jsx @@ -63,6 +63,9 @@ export default function useUpdateSiteSetting(name) { case 'Maintenance': toast.success(t('toast.success.site_settings.maintenance_updated')); break; + case 'AllowedDomains': + toast.success(t('toast.success.site_settings.allowed_domains_signup_updated')); + break; default: toast.success(t('toast.success.site_settings.site_setting_updated')); } diff --git a/app/services/tenant_setup.rb b/app/services/tenant_setup.rb index 47a8b8651f..2bad739f9c 100644 --- a/app/services/tenant_setup.rb +++ b/app/services/tenant_setup.rb @@ -56,7 +56,8 @@ def create_site_settings { setting: Setting.find_by(name: 'DefaultRole'), provider: @provider, value: 'User' }, { setting: Setting.find_by(name: 'DefaultRecordingVisibility'), provider: @provider, value: 'Published' }, { setting: Setting.find_by(name: 'Maintenance'), provider: @provider, value: '' }, - { setting: Setting.find_by(name: 'SessionTimeout'), provider: @provider, value: '1' } + { setting: Setting.find_by(name: 'SessionTimeout'), provider: @provider, value: '1' }, + { setting: Setting.find_by(name: 'AllowedDomains'), value: '', provider: @provider } ] end diff --git a/db/data/20240812210436_add_allowed_domains_to_site_settings.rb b/db/data/20240812210436_add_allowed_domains_to_site_settings.rb new file mode 100644 index 0000000000..5c1aa3f5bb --- /dev/null +++ b/db/data/20240812210436_add_allowed_domains_to_site_settings.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class AddAllowedDomainsToSiteSettings < ActiveRecord::Migration[7.1] + def up + setting = Setting.find_or_create_by(name: 'AllowedDomains') + + SiteSetting.create!(setting:, value: '', provider: 'greenlight') unless SiteSetting.exists?(setting:, provider: 'greenlight') + + Tenant.find_each do |tenant| + SiteSetting.create!(setting:, value: '', provider: tenant.name) unless SiteSetting.exists?(setting:, provider: tenant.name) + end + end + + def down + Tenant.find_each do |tenant| + SiteSetting.find_by(setting: Setting.find_by(name: 'Maintenance'), provider: tenant.name)&.destroy + end + + SiteSetting.find_by(setting: Setting.find_by(name: 'Maintenance'), provider: 'greenlight')&.destroy + + Setting.find_by(name: 'AllowedDomains')&.destroy + end +end diff --git a/db/data_schema.rb b/db/data_schema.rb index dd44530cd2..5f04068525 100644 --- a/db/data_schema.rb +++ b/db/data_schema.rb @@ -1 +1 @@ -DataMigrate::Data.define(version: 20240423162700) +DataMigrate::Data.define(version: 20240812210436) diff --git a/db/schema.rb b/db/schema.rb deleted file mode 100644 index dd09a891c4..0000000000 --- a/db/schema.rb +++ /dev/null @@ -1,224 +0,0 @@ -# This file is auto-generated from the current state of the database. Instead -# of editing this file, please use the migrations feature of Active Record to -# incrementally modify your database, and then regenerate this schema definition. -# -# This file is the source Rails uses to define your schema when running `bin/rails -# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to -# be faster and is potentially less error prone than running all of your -# migrations from scratch. Old migrations may fail to apply correctly if those -# migrations use external dependencies or application code. -# -# It's strongly recommended that you check this file into your version control system. - -ActiveRecord::Schema[7.1].define(version: 2023_12_18_154727) do - # These are extensions that must be enabled in order to support this database - enable_extension "pgcrypto" - enable_extension "plpgsql" - - create_table "active_storage_attachments", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "name", null: false - t.string "record_type", null: false - t.uuid "record_id", null: false - t.uuid "blob_id", null: false - t.datetime "created_at", null: false - t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" - t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true - end - - create_table "active_storage_blobs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "key", null: false - t.string "filename", null: false - t.string "content_type" - t.text "metadata" - t.string "service_name", null: false - t.bigint "byte_size", null: false - t.string "checksum" - t.datetime "created_at", null: false - t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true - end - - create_table "active_storage_variant_records", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.uuid "blob_id", null: false - t.string "variation_digest", null: false - t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true - end - - create_table "data_migrations", primary_key: "version", id: :string, force: :cascade do |t| - end - - create_table "formats", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.uuid "recording_id" - t.string "recording_type", null: false - t.string "url", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["recording_id"], name: "index_formats_on_recording_id" - end - - create_table "invitations", force: :cascade do |t| - t.string "email", null: false - t.string "provider", null: false - t.string "token", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["email", "provider"], name: "index_invitations_on_email_and_provider", unique: true - t.index ["token"], name: "index_invitations_on_token", unique: true - end - - create_table "meeting_options", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "name" - t.string "default_value" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["name"], name: "index_meeting_options_on_name", unique: true - end - - create_table "permissions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "name", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - end - - create_table "recordings", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.uuid "room_id" - t.string "name", null: false - t.string "record_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.string "visibility", null: false - t.integer "length", null: false - t.integer "participants", null: false - t.boolean "protectable" - t.datetime "recorded_at" - t.index ["room_id"], name: "index_recordings_on_room_id" - end - - create_table "role_permissions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.uuid "role_id" - t.uuid "permission_id" - t.string "value", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["permission_id"], name: "index_role_permissions_on_permission_id" - t.index ["role_id"], name: "index_role_permissions_on_role_id" - end - - create_table "roles", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "name", null: false - t.string "color", default: "", null: false - t.string "provider", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["name", "provider"], name: "index_roles_on_name_and_provider", unique: true - end - - create_table "room_meeting_options", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.uuid "room_id" - t.uuid "meeting_option_id" - t.string "value" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["meeting_option_id"], name: "index_room_meeting_options_on_meeting_option_id" - t.index ["room_id"], name: "index_room_meeting_options_on_room_id" - end - - create_table "rooms", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.uuid "user_id" - t.string "name", null: false - t.string "friendly_id", null: false - t.string "meeting_id", null: false - t.datetime "last_session" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.integer "recordings_processing", default: 0 - t.boolean "online", default: false - t.index ["friendly_id"], name: "index_rooms_on_friendly_id", unique: true - t.index ["meeting_id"], name: "index_rooms_on_meeting_id", unique: true - t.index ["user_id"], name: "index_rooms_on_user_id" - end - - create_table "rooms_configurations", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.uuid "meeting_option_id" - t.string "provider", null: false - t.string "value", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["meeting_option_id", "provider"], name: "index_rooms_configurations_on_meeting_option_id_and_provider", unique: true - t.index ["meeting_option_id"], name: "index_rooms_configurations_on_meeting_option_id" - end - - create_table "settings", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "name" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["name"], name: "index_settings_on_name", unique: true - end - - create_table "shared_accesses", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.uuid "user_id", null: false - t.uuid "room_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["room_id"], name: "index_shared_accesses_on_room_id" - t.index ["user_id", "room_id"], name: "index_shared_accesses_on_user_id_and_room_id", unique: true - t.index ["user_id"], name: "index_shared_accesses_on_user_id" - end - - create_table "site_settings", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.uuid "setting_id" - t.string "value", null: false - t.string "provider", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["setting_id"], name: "index_site_settings_on_setting_id" - end - - create_table "tenants", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "name", null: false - t.string "client_secret", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["name"], name: "index_tenants_on_name", unique: true - end - - create_table "users", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.string "name", null: false - t.string "email", null: false - t.string "external_id" - t.string "provider", null: false - t.string "password_digest" - t.datetime "last_login" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.uuid "role_id" - t.string "language", null: false - t.string "reset_digest" - t.datetime "reset_sent_at", precision: nil - t.boolean "verified", default: false - t.string "verification_digest" - t.datetime "verification_sent_at", precision: nil - t.string "session_token" - t.datetime "session_expiry", precision: nil - t.integer "status", default: 0 - t.index ["email", "provider"], name: "index_users_on_email_and_provider", unique: true - t.index ["reset_digest"], name: "index_users_on_reset_digest", unique: true - t.index ["role_id"], name: "index_users_on_role_id" - t.index ["session_token"], name: "index_users_on_session_token", unique: true - t.index ["verification_digest"], name: "index_users_on_verification_digest", unique: true - end - - add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" - add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" - add_foreign_key "formats", "recordings" - add_foreign_key "recordings", "rooms" - add_foreign_key "role_permissions", "permissions" - add_foreign_key "role_permissions", "roles" - add_foreign_key "room_meeting_options", "meeting_options" - add_foreign_key "room_meeting_options", "rooms" - add_foreign_key "rooms", "users" - add_foreign_key "rooms_configurations", "meeting_options" - add_foreign_key "shared_accesses", "rooms" - add_foreign_key "shared_accesses", "users" - add_foreign_key "site_settings", "settings" - add_foreign_key "users", "roles" -end diff --git a/spec/controllers/admin/tenants_controller_spec.rb b/spec/controllers/admin/tenants_controller_spec.rb index dfd13e8e93..a54876a60d 100644 --- a/spec/controllers/admin/tenants_controller_spec.rb +++ b/spec/controllers/admin/tenants_controller_spec.rb @@ -18,7 +18,7 @@ require 'rails_helper' -RSpec.describe Api::V1::Admin::TenantsController, type: :controller do +RSpec.describe Api::V1::Admin::TenantsController do let(:user) { create(:user, :with_super_admin) } let(:valid_tenant_params) do { @@ -146,6 +146,7 @@ def create_settings_permissions_meetingoptions Setting.find_or_create_by(name: 'HelpCenter') Setting.find_or_create_by(name: 'Maintenance') Setting.find_or_create_by(name: 'SessionTimeout') + Setting.find_or_create_by(name: 'AllowedDomains') Permission.find_or_create_by(name: 'CreateRoom') Permission.find_or_create_by(name: 'ManageUsers') diff --git a/spec/controllers/external_controller_spec.rb b/spec/controllers/external_controller_spec.rb index 607dac434f..764690dbc1 100644 --- a/spec/controllers/external_controller_spec.rb +++ b/spec/controllers/external_controller_spec.rb @@ -18,7 +18,7 @@ require 'rails_helper' -RSpec.describe ExternalController, type: :controller do +RSpec.describe ExternalController do let(:fake_setting_getter) { instance_double(SettingGetter) } describe '#create_user' do @@ -80,7 +80,7 @@ expect do get :create_user, params: { provider: 'openid_connect' } - end.to change(User, :count).by(0) + end.not_to change(User, :count) end it 'looks the user up based on email' do @@ -90,7 +90,7 @@ expect do get :create_user, params: { provider: 'openid_connect' } - end.to change(User, :count).by(0) + end.not_to change(User, :count) end context 'redirect' do @@ -212,40 +212,52 @@ email: 'email@example.com') end - it 'overwrites the saved values with the values from the authentication provider if true' do - allow_any_instance_of(SettingGetter).to receive(:call).and_return(true) + context 'value is true' do + before do + reg_method = instance_double(SettingGetter) + allow(SettingGetter).to receive(:new).with(setting_name: 'ResyncOnLogin', provider: 'greenlight').and_return(reg_method) + allow(reg_method).to receive(:call).and_return(true) + end - request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:openid_connect] + it 'overwrites the saved values with the values from the authentication provider if true' do + request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:openid_connect] - get :create_user, params: { provider: 'openid_connect' } + get :create_user, params: { provider: 'openid_connect' } - user.reload - expect(user.name).to eq(OmniAuth.config.mock_auth[:openid_connect]['info']['name']) - expect(user.email).to eq(OmniAuth.config.mock_auth[:openid_connect]['info']['email']) - end + user.reload + expect(user.name).to eq(OmniAuth.config.mock_auth[:openid_connect]['info']['name']) + expect(user.email).to eq(OmniAuth.config.mock_auth[:openid_connect]['info']['email']) + end - it 'does not overwrite the saved values with the values from the authentication provider if false' do - allow_any_instance_of(SettingGetter).to receive(:call).and_return(false) + it 'does not overwrite the role even if true' do + allow_any_instance_of(SettingGetter).to receive(:call).and_return(true) + request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:openid_connect] - request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:openid_connect] + new_role = create(:role) + user.update(role: new_role) - get :create_user, params: { provider: 'openid_connect' } + get :create_user, params: { provider: 'openid_connect' } - user.reload - expect(user.name).to eq('Example Name') - expect(user.email).to eq('email@example.com') + expect(user.reload.role).to eq(new_role) + end end - it 'does not overwrite the role even if true' do - allow_any_instance_of(SettingGetter).to receive(:call).and_return(true) - request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:openid_connect] + context 'value is false' do + before do + reg_method = instance_double(SettingGetter) + allow(SettingGetter).to receive(:new).with(setting_name: 'ResyncOnLogin', provider: 'greenlight').and_return(reg_method) + allow(reg_method).to receive(:call).and_return(false) + end - new_role = create(:role) - user.update(role: new_role) + it 'does not overwrite the saved values with the values from the authentication provider if false' do + request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:openid_connect] - get :create_user, params: { provider: 'openid_connect' } + get :create_user, params: { provider: 'openid_connect' } - expect(user.reload.role).to eq(new_role) + user.reload + expect(user.name).to eq('Example Name') + expect(user.email).to eq('email@example.com') + end end end @@ -325,6 +337,79 @@ end end + context 'Allowed Domains' do + context 'restricted domain not set' do + before do + site_settings = instance_double(SettingGetter) + allow(SettingGetter).to receive(:new).with(setting_name: 'AllowedDomains', provider: 'greenlight').and_return(site_settings) + allow(site_settings).to receive(:call).and_return('') + end + + it 'creates the user' do + request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:openid_connect] + + expect { get :create_user, params: { provider: 'openid_connect' } }.to change(User, :count).from(0).to(1) + end + end + + context 'restricted domain set to 1 domain' do + before do + site_settings = instance_double(SettingGetter) + allow(SettingGetter).to receive(:new).with(setting_name: 'AllowedDomains', provider: 'greenlight').and_return(site_settings) + allow(site_settings).to receive(:call).and_return('@domain.com') + end + + it 'creates the user if the domain is allowed' do + request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:openid_connect] + request.env['omniauth.auth'][:info][:email] = 'email@domain.com' + + expect { get :create_user, params: { provider: 'openid_connect' } }.to change(User, :count).from(0).to(1) + end + + it 'does not create if the domain is not allowed' do + request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:openid_connect] + + expect { get :create_user, params: { provider: 'openid_connect' } }.not_to change(User, :count) + end + end + + context 'restricted domain set to multiple domain' do + before do + site_settings = instance_double(SettingGetter) + allow(SettingGetter).to receive(:new).with(setting_name: 'AllowedDomains', provider: 'greenlight').and_return(site_settings) + allow(site_settings).to receive(:call).and_return('@example.com,@test.com,@domain.com') + end + + it 'creates the user if the domain is allowed 1' do + request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:openid_connect] + request.env['omniauth.auth'][:info][:email] = 'email@example.com' + + expect { get :create_user, params: { provider: 'openid_connect' } }.to change(User, :count).from(0).to(1) + end + + it 'creates the user if the domain is allowed 2' do + request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:openid_connect] + request.env['omniauth.auth'][:info][:email] = 'email@test.com' + + expect { get :create_user, params: { provider: 'openid_connect' } }.to change(User, :count).from(0).to(1) + end + + it 'creates the user if the domain is allowed 3' do + request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:openid_connect] + request.env['omniauth.auth'][:info][:email] = 'email@domain.com' + + expect { get :create_user, params: { provider: 'openid_connect' } }.to change(User, :count).from(0).to(1) + end + + it 'does not create if the domain is not allowed' do + request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:openid_connect] + request.env['omniauth.auth'][:info][:email] = 'test@invaliddomain.com' + + expect { get :create_user, params: { provider: 'openid_connect' } }.not_to change(User, :count) + end + end + end + context 'Role mapping' do let!(:role1) { create(:role, name: 'role1') } diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 81a9d594ec..d24017fc3b 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -301,6 +301,66 @@ expect(response.parsed_body['errors']).not_to be_nil end end + + context 'Allowed Domains' do + context 'restricted domain not set' do + before do + site_settings = instance_double(SettingGetter) + allow(SettingGetter).to receive(:new).with(setting_name: 'AllowedDomains', provider: 'greenlight').and_return(site_settings) + allow(site_settings).to receive(:call).and_return('') + end + + it 'creates the user' do + expect { post :create, params: user_params }.to change(User, :count).from(0).to(1) + end + end + + context 'restricted domain set to 1 domain' do + before do + site_settings = instance_double(SettingGetter) + allow(SettingGetter).to receive(:new).with(setting_name: 'AllowedDomains', provider: 'greenlight').and_return(site_settings) + allow(site_settings).to receive(:call).and_return('@domain.com') + end + + it 'creates the user if the domain is allowed' do + user_params[:user][:email] = 'test@domain.com' + expect { post :create, params: user_params }.to change(User, :count).from(0).to(1) + end + + it 'does not create if the domain is not allowed' do + user_params[:user][:email] = 'test@invaliddomain.com' + expect { post :create, params: user_params }.not_to change(User, :count) + end + end + + context 'restricted domain set to multiple domain' do + before do + site_settings = instance_double(SettingGetter) + allow(SettingGetter).to receive(:new).with(setting_name: 'AllowedDomains', provider: 'greenlight').and_return(site_settings) + allow(site_settings).to receive(:call).and_return('@example.com,@test.com,@domain.com') + end + + it 'creates the user if the domain is allowed 1' do + user_params[:user][:email] = 'test@example.com' + expect { post :create, params: user_params }.to change(User, :count).from(0).to(1) + end + + it 'creates the user if the domain is allowed 2' do + user_params[:user][:email] = 'test@test.com' + expect { post :create, params: user_params }.to change(User, :count).from(0).to(1) + end + + it 'creates the user if the domain is allowed 3' do + user_params[:user][:email] = 'test@domain.com' + expect { post :create, params: user_params }.to change(User, :count).from(0).to(1) + end + + it 'does not create if the domain is not allowed' do + user_params[:user][:email] = 'test@invaliddomain.com' + expect { post :create, params: user_params }.not_to change(User, :count) + end + end + end end describe '#show' do