diff --git a/Gemfile b/Gemfile index 4b702260d44836..9f8c69267aba53 100644 --- a/Gemfile +++ b/Gemfile @@ -145,9 +145,6 @@ group :test do # Used to mock environment variables gem 'climate_control' - # Add back helpers functions removed in Rails 5.1 - gem 'rails-controller-testing', '~> 1.0' - # Validate schemas in specs gem 'json-schema', '~> 5.0' diff --git a/Gemfile.lock b/Gemfile.lock index 9f8257891f607d..19ddbbf8d79ff2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -194,7 +194,7 @@ GEM devise_pam_authenticatable2 (9.2.0) devise (>= 4.0.0) rpam2 (~> 4.0) - diff-lcs (1.5.1) + diff-lcs (1.6.0) discard (1.4.0) activerecord (>= 4.2, < 9.0) docile (1.4.1) @@ -409,11 +409,11 @@ GEM mime-types (3.6.0) logger mime-types-data (~> 3.2015) - mime-types-data (3.2025.0204) + mime-types-data (3.2025.0220) mini_mime (1.1.5) mini_portile2 (2.8.8) minitest (5.25.4) - msgpack (1.7.5) + msgpack (1.8.0) multi_json (1.15.0) mutex_m (0.3.0) net-http (0.6.0) @@ -429,7 +429,7 @@ GEM net-smtp (0.5.1) net-protocol nio4r (2.7.4) - nokogiri (1.18.2) + nokogiri (1.18.3) mini_portile2 (~> 2.8.2) racc (~> 1.4) oj (3.16.9) @@ -641,10 +641,6 @@ GEM activesupport (= 8.0.1) bundler (>= 1.15.0) railties (= 8.0.1) - rails-controller-testing (1.0.5) - actionpack (>= 5.0.1.rc1) - actionview (>= 5.0.1.rc1) - activesupport (>= 5.0.1.rc1) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest @@ -687,7 +683,7 @@ GEM responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) - rexml (3.4.0) + rexml (3.4.1) rotp (6.3.0) rouge (4.5.1) rpam2 (4.0.2) @@ -774,7 +770,7 @@ GEM activerecord (>= 4.0.0) railties (>= 4.0.0) securerandom (0.4.1) - selenium-webdriver (4.28.0) + selenium-webdriver (4.29.0) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) @@ -814,7 +810,7 @@ GEM stackprof (0.2.27) stoplight (4.1.1) redlock (~> 1.0) - stringio (3.1.2) + stringio (3.1.4) strong_migrations (2.2.0) activerecord (>= 7) swd (1.3.0) @@ -898,7 +894,7 @@ GEM xorcist (1.1.3) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.7.1) + zeitwerk (2.7.2) PLATFORMS ruby @@ -1009,7 +1005,6 @@ DEPENDENCIES rack-cors (~> 2.0) rack-test (~> 2.1) rails (~> 8.0) - rails-controller-testing (~> 1.0) rails-i18n (~> 8.0) rdf-normalize (~> 0.5) redcarpet (~> 3.6) @@ -1059,4 +1054,4 @@ RUBY VERSION ruby 3.4.1p0 BUNDLED WITH - 2.6.3 + 2.6.5 diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 93e485f0960544..83699b34ede5bc 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -697,7 +697,7 @@ "poll_button.remove_poll": "Remove poll", "privacy.change": "Change post privacy", "privacy.direct.long": "Everyone mentioned in the post", - "privacy.direct.short": "Specific people", + "privacy.direct.short": "Private mention", "privacy.private.long": "Only your followers", "privacy.private.short": "Followers", "privacy.public.long": "Anyone on and off Mastodon", diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json index 53a714d7a5104e..5c3b283bd91450 100644 --- a/app/javascript/mastodon/locales/kab.json +++ b/app/javascript/mastodon/locales/kab.json @@ -5,7 +5,7 @@ "about.domain_blocks.no_reason_available": "Ulac taɣẓint", "about.domain_blocks.preamble": "Maṣṭudun s umata yeḍmen-ak ad teẓreḍ agbur, ad tesdemreḍ akked yimseqdacen-nniḍen seg yal aqeddac deg fedivers. Ha-tent-an ɣur-k tsuraf i yellan deg uqeddac-agi.", "about.domain_blocks.silenced.title": "Ɣur-s talast", - "about.domain_blocks.suspended.title": "Yeḥbes", + "about.domain_blocks.suspended.title": "Yettwaḥbes", "about.not_available": "Talɣut-a ur tettwabder ara deg uqeddac-a.", "about.powered_by": "Azeṭṭa inmetti yettwasɣelsen sɣur {mastodon}", "about.rules": "Ilugan n uqeddac", @@ -264,6 +264,7 @@ "footer.privacy_policy": "Tasertit tabaḍnit", "footer.source_code": "Wali tangalt taɣbalut", "footer.status": "Addad", + "footer.terms_of_service": "Tiwtilin n useqdec", "generic.saved": "Yettwasekles", "getting_started.heading": "Bdu", "hashtag.column_header.tag_mode.all": "d {additional}", @@ -623,6 +624,7 @@ "subscribed_languages.save": "Sekles ibeddilen", "tabs_bar.home": "Agejdan", "tabs_bar.notifications": "Alɣuten", + "terms_of_service.title": "Tiwtilin n useqdec", "time_remaining.days": "Mazal {number, plural, one {# wass} other {# wussan}}", "time_remaining.hours": "Mazal {number, plural, one {# usarag} other {# yisragen}}", "time_remaining.minutes": "Mazal {number, plural, one {# n tesdat} other {# n tesdatin}}", diff --git a/app/javascript/mastodon/locales/lad.json b/app/javascript/mastodon/locales/lad.json index ed91aaccfe62ff..f3baeb5927768d 100644 --- a/app/javascript/mastodon/locales/lad.json +++ b/app/javascript/mastodon/locales/lad.json @@ -86,6 +86,9 @@ "alert.unexpected.message": "Afito un yerro no asperado.", "alert.unexpected.title": "Atyo!", "alt_text_badge.title": "Teksto alternativo", + "alt_text_modal.cancel": "Anula", + "alt_text_modal.change_thumbnail": "Troka minyatura", + "alt_text_modal.done": "Fecho", "announcement.announcement": "Pregon", "annual_report.summary.archetype.pollster": "El anketero", "annual_report.summary.followers.followers": "suivantes", @@ -129,9 +132,11 @@ "column.blocks": "Utilizadores blokados", "column.bookmarks": "Markadores", "column.community": "Linya lokala", + "column.create_list": "Kriya lista", "column.direct": "Enmentaduras privadas", "column.directory": "Eksplora profiles", "column.domain_blocks": "Domenos blokados", + "column.edit_list": "Edita lista", "column.favourites": "Te plazen", "column.firehose": "Linyas en bivo", "column.follow_requests": "Solisitudes de segimiento", @@ -148,6 +153,7 @@ "column_header.pin": "Fiksa", "column_header.show_settings": "Amostra opsyones", "column_header.unpin": "Defiksar", + "column_search.cancel": "Anula", "column_subheading.settings": "Opsyones", "community.column_settings.local_only": "Solo lokalas", "community.column_settings.media_only": "Solo multimedia", @@ -219,6 +225,8 @@ "disabled_account_banner.text": "Tu kuento {disabledAccount} esta aktualmente inkapasitado.", "dismissable_banner.community_timeline": "Estas son las publikasyones publikas mas resientes de las personas kualos kuentos estan balabayados en {domain}.", "dismissable_banner.dismiss": "Kita", + "dismissable_banner.explore_statuses": "Estas publikasyones del fediverso estan agora popularas. Publikasyones mas muevas, kon mas repartajasiones i favoritadas por mas djente aparesen primero.", + "dismissable_banner.public_timeline": "Estas son las publikasyones publikas mas resientes de personas en el fediverso a las kualas la djente de {domain} sige.", "domain_block_modal.block": "Bloka sirvidor", "domain_block_modal.block_account_instead": "Bloka @{name} en su lugar", "domain_block_modal.they_can_interact_with_old_posts": "Las personas de este sirvidor pueden enteraktuar kon tus puvlikasyones viejas.", @@ -327,6 +335,7 @@ "footer.status": "Estado", "generic.saved": "Guadrado", "getting_started.heading": "Primos pasos", + "hashtag.admin_moderation": "Avre la enterfaz de moderasyon para #{name}", "hashtag.column_header.tag_mode.all": "i {additional}", "hashtag.column_header.tag_mode.any": "o {additional}", "hashtag.column_header.tag_mode.none": "sin {additional}", @@ -415,11 +424,17 @@ "link_preview.author": "Publikasyon de {name}", "link_preview.more_from_author": "Mas de {name}", "link_preview.shares": "{count, plural, one {{counter} publikasyon} other {{counter} publikasyones}}", + "lists.add_member": "Adjusta", + "lists.add_to_list": "Adjusta a lista", + "lists.create_list": "Kriya lista", "lists.delete": "Efasa lista", + "lists.done": "Fecho", "lists.edit": "Edita lista", "lists.replies_policy.followed": "Kualseker utilizador segido", "lists.replies_policy.list": "Miembros de la lista", "lists.replies_policy.none": "Dinguno", + "lists.save": "Guadra", + "lists.search": "Bushka", "load_pending": "{count, plural, one {# muevo elemento} other {# muevos elementos}}", "loading_indicator.label": "Eskargando…", "media_gallery.hide": "Eskonde", @@ -544,7 +559,10 @@ "notifications_permission_banner.enable": "Kapasita avizos de ensimameza", "notifications_permission_banner.how_to_control": "Para risivir avizos kuando Mastodon no esta avierto, kapasita avizos de ensimameza. Puedes kontrolar presizamente kualos tipos de enteraksiones djeneren avizos de ensimameza kon el boton {icon} arriva kuando esten kapasitadas.", "notifications_permission_banner.title": "Nunkua te piedres niente", + "onboarding.follows.back": "Atras", + "onboarding.follows.done": "Fecho", "onboarding.follows.empty": "Malorozamente, no se pueden amostrar rezultados en este momento. Puedes aprovar uzar la bushkeda o navigar por la pajina de eksplorasyon para topar personas a las que segir, o aprovarlo de muevo mas tadre.", + "onboarding.follows.search": "Bushka", "onboarding.profile.discoverable": "Faz ke mi profil apareska en bushkedas", "onboarding.profile.discoverable_hint": "Kuando permites ke tu profil sea diskuvriravle en Mastodon, tus publikasyones podran apareser en rezultados de bushkedas i trendes i tu profil podra ser sujerido a personas kon intereses similares a los tuyos.", "onboarding.profile.display_name": "Nombre amostrado", @@ -667,6 +685,7 @@ "search_results.hashtags": "Etiketas", "search_results.see_all": "Ve todo", "search_results.statuses": "Publikasyones", + "search_results.title": "Bushka por \"{q}\"", "server_banner.about_active_users": "Utilizadores aktivos en este sirvidor durante los ultimos 30 diyas (utilizadores aktivos mensuales)", "server_banner.active_users": "utilizadores aktivos", "server_banner.administered_by": "Administrado por:", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index e48b02aaf3eee2..58acdb8c281c14 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -52,7 +52,7 @@ "account.mute_notifications_short": "Wycisz powiadomienia", "account.mute_short": "Wycisz", "account.muted": "Wyciszony", - "account.mutual": "Przyjaciele", + "account.mutual": "Znajomi", "account.no_bio": "Brak opisu.", "account.open_original_page": "Otwórz stronę oryginalną", "account.posts": "Wpisy", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index 6ca6b0e3291ab5..c44a97f0e9fc05 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -379,6 +379,7 @@ "ignore_notifications_modal.filter_to_act_users": "Stále budeš môcť akceptovať, odmietnuť, alebo nahlásiť užívateľov", "ignore_notifications_modal.filter_to_avoid_confusion": "Triedenie pomáha vyvarovať sa možnému zmäteniu", "ignore_notifications_modal.ignore": "Ignoruj upozornenia", + "ignore_notifications_modal.limited_accounts_title": "Ignorovať oboznámenia z obmedzených účtov?", "ignore_notifications_modal.new_accounts_title": "Nevšímať si oznámenia z nových účtov?", "ignore_notifications_modal.not_followers_title": "Nevšímať si oznámenia od ľudí, ktorí ťa nenasledujú?", "ignore_notifications_modal.not_following_title": "Nevšímať si oznámenia od ľudí, ktorých nenasleduješ?", diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json index 88e926ffb17975..b43e76cbf09412 100644 --- a/app/javascript/mastodon/locales/vi.json +++ b/app/javascript/mastodon/locales/vi.json @@ -743,12 +743,12 @@ "report.mute_explanation": "Bạn sẽ không còn thấy tút của người này. Họ vẫn có thể thấy tút của bạn hoặc theo dõi bạn. Họ không biết là bạn đã chặn họ.", "report.next": "Tiếp theo", "report.placeholder": "Thêm lưu ý", - "report.reasons.dislike": "Tôi không thích nó", - "report.reasons.dislike_description": "Đó không phải là thứ gì mà bạn muốn thấy", + "report.reasons.dislike": "Tôi không thích", + "report.reasons.dislike_description": "Đây không phải thứ mà bạn muốn thấy", "report.reasons.legal": "Vi phạm pháp luật", "report.reasons.legal_description": "Vi phạm pháp luật ở nơi đặt máy chủ hoặc nước bạn", - "report.reasons.other": "Một lý do khác", - "report.reasons.other_description": "Vấn đề không nằm trong những mục trên", + "report.reasons.other": "Lý do khác", + "report.reasons.other_description": "Vấn đề không thuộc những mục trên", "report.reasons.spam": "Đây là spam", "report.reasons.spam_description": "Liên kết độc hại, giả tương tác hoặc trả lời lặp đi lặp lại", "report.reasons.violation": "Vi phạm nội quy máy chủ", diff --git a/config/locales/eo.yml b/config/locales/eo.yml index 98930d11baecf5..6947d240f804d2 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -1777,7 +1777,7 @@ eo: migrate: Konta migrado notifications: Retpoŝtaj sciigoj preferences: Preferoj - profile: Profilo + profile: Publika profilo relationships: Sekvatoj kaj sekvantoj severed_relationships: Finitaj rilatoj statuses_cleanup: Automata mesaĝforigo diff --git a/config/locales/kab.yml b/config/locales/kab.yml index 6232a7eba4674e..b65109df1a6d5d 100644 --- a/config/locales/kab.yml +++ b/config/locales/kab.yml @@ -71,7 +71,7 @@ kab: active: Yermed all: Akk pending: Yettraǧu - suspended: Yeḥbes + suspended: Yettwaḥbes title: Aseɣyed moderation_notes: Tamawin n useɣyed most_recent_activity: Armud aneggaru @@ -109,7 +109,7 @@ kab: silenced: Yettwasgugem statuses: Tisuffaɣ subscribe: Jerred - suspended: Yeḥbes + suspended: Yettwaḥbes title: Imiḍanen unconfirmed_email: Imayl ur yettwasentem ara undo_silenced: Kkes asgugem @@ -434,9 +434,12 @@ kab: search: Anadi title: Ihacṭagen terms_of_service: + changelog: Amaynut draft: Arewway + history: Amazray publish: Asuffeɣ save_draft: Sekles arewway + title: Tiwtilin n useqdec title: Tadbelt trends: allow: Sireg @@ -832,6 +835,8 @@ kab: '7889238': 3 n wayyuren stream_entries: sensitive_content: Agbur amḥulfu + terms_of_service: + title: Tiwtilin n useqdec themes: contrast: Maṣṭudun (agnil awriran) default: Maṣṭudun (Aberkan) @@ -854,6 +859,8 @@ kab: user_mailer: appeal_approved: action: Iɣewwaṛen n umiḍan + terms_of_service_changed: + sign_off: Agraw n %{domain} warning: categories: spam: Aspam diff --git a/config/locales/lad.yml b/config/locales/lad.yml index 939a8826b29b10..86dbc668c06dbe 100644 --- a/config/locales/lad.yml +++ b/config/locales/lad.yml @@ -812,6 +812,7 @@ lad: batch: remove_from_report: Kita del raporto report: Raporto + contents: Kontenidos deleted: Efasado favourites: Favoritos history: Estoria de versiones @@ -901,6 +902,9 @@ lad: search: Bushka title: Etiketas updated_msg: Konfigurasyon de etiketas aktualizada kon sukseso + terms_of_service: + live: En bivo + publish: Publika title: Administrasyon trends: allow: Permete @@ -1129,6 +1133,7 @@ lad: title: Konektate kon %{domain} sign_up: manual_review: Las enrejistrasyones en %{domain} pasan por la revizyon manuala de muestros moderadores. Para ayudarmos a prosesar tu enrejistrasyon, eskrive un poko sovre ti i por ke keres un kuento en %{domain}. + preamble: Kon un kuento en este sirvidor de Mastodon, podras segir a kualkier otra persona en el fediverso, endependientemente del sirvidor en el ke se tope. title: Kriya kuento de Mastodon en %{domain}. status: account_status: Estado del kuento diff --git a/config/locales/simple_form.eo.yml b/config/locales/simple_form.eo.yml index 8103f739291643..b0b8e4e4d7df76 100644 --- a/config/locales/simple_form.eo.yml +++ b/config/locales/simple_form.eo.yml @@ -193,7 +193,7 @@ eo: text: Klarigu kial ĉi tiu decido devas inversigitis defaults: autofollow: Inviti al sekvi vian konton - avatar: Rolfiguro + avatar: Profilbildo bot: Ĉi tio estas aŭtomata konto chosen_languages: Filtri lingvojn confirm_new_password: Konfirmi novan pasvorton diff --git a/config/locales/simple_form.gl.yml b/config/locales/simple_form.gl.yml index 1c3fa2278da6fd..412e0eb03d8889 100644 --- a/config/locales/simple_form.gl.yml +++ b/config/locales/simple_form.gl.yml @@ -281,7 +281,7 @@ gl: site_terms: Política de Privacidade site_title: Nome do servidor status_page_url: URL da páxina do estado - theme: Decorado por defecto + theme: Decorado predeterminado thumbnail: Icona do servidor timeline_preview: Permitir acceso á cronoloxía pública sen autenticación trendable_by_default: Permitir tendencias sen aprobación previa diff --git a/config/locales/simple_form.kab.yml b/config/locales/simple_form.kab.yml index ff041b257b43ad..c14f6376a0664c 100644 --- a/config/locales/simple_form.kab.yml +++ b/config/locales/simple_form.kab.yml @@ -141,6 +141,8 @@ kab: text: Alugen tag: name: Ahacṭag + terms_of_service: + text: Tiwtilin n useqdec user: role: Tamlilt time_zone: Tamnaḍt tasragant diff --git a/config/locales/simple_form.lad.yml b/config/locales/simple_form.lad.yml index de37005312d456..6fef9f7422280d 100644 --- a/config/locales/simple_form.lad.yml +++ b/config/locales/simple_form.lad.yml @@ -308,6 +308,8 @@ lad: name: Etiketa trendable: Permite ke esta etiketa apareska en trendes usable: Permite ke publikasyones uzen esta etiketa lokalmente + terms_of_service_generator: + domain: Domeno user: role: Rolo time_zone: Zona de tiempo diff --git a/spec/controllers/admin/domain_blocks_controller_spec.rb b/spec/controllers/admin/domain_blocks_controller_spec.rb index f3f08c79405695..bf3737bdb37dc7 100644 --- a/spec/controllers/admin/domain_blocks_controller_spec.rb +++ b/spec/controllers/admin/domain_blocks_controller_spec.rb @@ -69,7 +69,8 @@ expect(DomainBlockWorker).to_not have_received(:perform_async) - expect(response).to render_template :new + expect(response.parsed_body.title) + .to match(I18n.t('admin.domain_blocks.new.title')) end end @@ -84,7 +85,8 @@ expect(DomainBlockWorker).to_not have_received(:perform_async) - expect(response).to render_template :confirm_suspension + expect(response.parsed_body.title) + .to match(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'example.com')) end end @@ -119,7 +121,8 @@ expect(DomainBlockWorker).to_not have_received(:perform_async) - expect(response).to render_template :confirm_suspension + expect(response.parsed_body.title) + .to match(I18n.t('admin.domain_blocks.confirm_suspension.title', domain: 'example.com')) end end diff --git a/spec/controllers/admin/roles_controller_spec.rb b/spec/controllers/admin/roles_controller_spec.rb deleted file mode 100644 index 173a89e5d5d6ef..00000000000000 --- a/spec/controllers/admin/roles_controller_spec.rb +++ /dev/null @@ -1,235 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Admin::RolesController do - render_views - - let(:permissions) { UserRole::Flags::NONE } - let(:current_role) { UserRole.create(name: 'Foo', permissions: permissions, position: 10) } - let(:current_user) { Fabricate(:user, role: current_role) } - - before do - sign_in current_user, scope: :user - end - - describe 'GET #index' do - before do - get :index - end - - context 'when user does not have permission to manage roles' do - it 'returns http forbidden' do - expect(response).to have_http_status(403) - end - end - - context 'when user has permission to manage roles' do - let(:permissions) { UserRole::FLAGS[:manage_roles] } - - it 'returns http success' do - expect(response).to have_http_status(:success) - end - end - end - - describe 'GET #new' do - before do - get :new - end - - context 'when user does not have permission to manage roles' do - it 'returns http forbidden' do - expect(response).to have_http_status(403) - end - end - - context 'when user has permission to manage roles' do - let(:permissions) { UserRole::FLAGS[:manage_roles] } - - it 'returns http success' do - expect(response).to have_http_status(:success) - end - end - end - - describe 'POST #create' do - let(:selected_position) { 1 } - let(:selected_permissions_as_keys) { %w(manage_roles) } - - before do - post :create, params: { user_role: { name: 'Bar', position: selected_position, permissions_as_keys: selected_permissions_as_keys } } - end - - context 'when user has permission to manage roles' do - let(:permissions) { UserRole::FLAGS[:manage_roles] } - - context 'when new role\'s does not elevate above the user\'s role' do - let(:selected_position) { 1 } - let(:selected_permissions_as_keys) { %w(manage_roles) } - - it 'redirects to roles page and creates role' do - expect(response).to redirect_to(admin_roles_path) - - expect(UserRole.find_by(name: 'Bar')).to_not be_nil - end - end - - context 'when new role\'s position is higher than user\'s role' do - let(:selected_position) { 100 } - let(:selected_permissions_as_keys) { %w(manage_roles) } - - it 'renders new template and does not create role' do - expect(response).to render_template(:new) - - expect(UserRole.find_by(name: 'Bar')).to be_nil - end - end - - context 'when new role has permissions the user does not have' do - let(:selected_position) { 1 } - let(:selected_permissions_as_keys) { %w(manage_roles manage_users manage_reports) } - - it 'renders new template and does not create role' do - expect(response).to render_template(:new) - - expect(UserRole.find_by(name: 'Bar')).to be_nil - end - end - - context 'when user has administrator permission' do - let(:permissions) { UserRole::FLAGS[:administrator] } - - let(:selected_position) { 1 } - let(:selected_permissions_as_keys) { %w(manage_roles manage_users manage_reports) } - - it 'redirects to roles page and creates new role' do - expect(response).to redirect_to(admin_roles_path) - - expect(UserRole.find_by(name: 'Bar')).to_not be_nil - end - end - end - end - - describe 'GET #edit' do - let(:role_position) { 8 } - let(:role) { UserRole.create(name: 'Bar', permissions: UserRole::FLAGS[:manage_users], position: role_position) } - - before do - get :edit, params: { id: role.id } - end - - context 'when user does not have permission to manage roles' do - it 'returns http forbidden' do - expect(response).to have_http_status(403) - end - end - - context 'when user has permission to manage roles' do - let(:permissions) { UserRole::FLAGS[:manage_roles] } - - context 'when user outranks the role' do - it 'returns http success' do - expect(response).to have_http_status(:success) - end - end - - context 'when role outranks user' do - let(:role_position) { current_role.position + 1 } - - it 'returns http forbidden' do - expect(response).to have_http_status(403) - end - end - end - end - - describe 'PUT #update' do - let(:role_position) { 8 } - let(:role_permissions) { UserRole::FLAGS[:manage_users] } - let(:role) { UserRole.create(name: 'Bar', permissions: role_permissions, position: role_position) } - - let(:selected_position) { 8 } - let(:selected_permissions_as_keys) { %w(manage_users) } - - before do - put :update, params: { id: role.id, user_role: { name: 'Baz', position: selected_position, permissions_as_keys: selected_permissions_as_keys } } - end - - context 'when user does not have permission to manage roles' do - it 'returns http forbidden and does not update role' do - expect(response).to have_http_status(403) - - expect(role.reload.name).to eq 'Bar' - end - end - - context 'when user has permission to manage roles' do - let(:permissions) { UserRole::FLAGS[:manage_roles] } - - context 'when role has permissions the user doesn\'t' do - it 'renders edit template and does not update role' do - expect(response).to render_template(:edit) - - expect(role.reload.name).to eq 'Bar' - end - end - - context 'when user has all permissions of the role' do - let(:permissions) { UserRole::FLAGS[:manage_roles] | UserRole::FLAGS[:manage_users] } - - context 'when user outranks the role' do - it 'redirects to roles page and updates role' do - expect(response).to redirect_to(admin_roles_path) - - expect(role.reload.name).to eq 'Baz' - end - end - - context 'when role outranks user' do - let(:role_position) { current_role.position + 1 } - - it 'returns http forbidden and does not update role' do - expect(response).to have_http_status(403) - - expect(role.reload.name).to eq 'Bar' - end - end - end - end - end - - describe 'DELETE #destroy' do - let(:role_position) { 8 } - let(:role) { UserRole.create(name: 'Bar', permissions: UserRole::FLAGS[:manage_users], position: role_position) } - - before do - delete :destroy, params: { id: role.id } - end - - context 'when user does not have permission to manage roles' do - it 'returns http forbidden' do - expect(response).to have_http_status(403) - end - end - - context 'when user has permission to manage roles' do - let(:permissions) { UserRole::FLAGS[:manage_roles] } - - context 'when user outranks the role' do - it 'redirects to roles page' do - expect(response).to redirect_to(admin_roles_path) - end - end - - context 'when role outranks user' do - let(:role_position) { current_role.position + 1 } - - it 'returns http forbidden' do - expect(response).to have_http_status(403) - end - end - end - end -end diff --git a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb index 224310b7ef3043..45c5e773237c10 100644 --- a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb +++ b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb @@ -5,14 +5,14 @@ RSpec.describe Settings::TwoFactorAuthentication::ConfirmationsController do render_views - shared_examples 'renders :new' do - it 'renders the new view' do + shared_examples 'renders expected page' do + it 'renders the new view with QR code' do subject expect(response).to have_http_status(200) - expect(response).to render_template(:new) expect(response.body) .to include(qr_code_markup) + .and include(I18n.t('settings.two_factor_authentication')) end def qr_code_markup @@ -34,7 +34,7 @@ def qr_code_markup get :new, session: { challenge_passed_at: Time.now.utc, new_otp_secret: 'thisisasecretforthespecofnewview' } end - include_examples 'renders :new' + include_examples 'renders expected page' end it 'redirects if a new otp_secret has not been set in the session' do @@ -66,10 +66,13 @@ def qr_code_markup expect { post_create_with_options } .to change { user.reload.otp_secret }.to 'thisisasecretforthespecofnewview' - expect(flash[:notice]).to eq 'Two-factor authentication successfully enabled' - expect(response).to have_http_status(200) - expect(response).to render_template('settings/two_factor_authentication/recovery_codes/index') - expect(response.body).to include(*otp_backup_codes) + expect(flash[:notice]) + .to eq(I18n.t('two_factor_authentication.enabled_success')) + expect(response) + .to have_http_status(200) + expect(response.body) + .to include(*otp_backup_codes) + .and include(I18n.t('settings.two_factor_authentication')) end end @@ -86,10 +89,12 @@ def qr_code_markup it 'renders page with error message' do subject - expect(response.body).to include 'The entered code was invalid! Are server time and device time correct?' + + expect(response.body) + .to include(I18n.t('otp_authentication.wrong_code')) end - include_examples 'renders :new' + include_examples 'renders expected page' end private @@ -116,18 +121,4 @@ def prepare_user_otp_consumption_response(result) end end end - - context 'when not signed in' do - it 'redirects on POST to create' do - post :create, params: { form_two_factor_confirmation: { otp_attempt: '123456' } } - - expect(response).to redirect_to('/auth/sign_in') - end - - it 'redirects on GET to new' do - get :new - - expect(response).to redirect_to('/auth/sign_in') - end - end end diff --git a/spec/requests/admin/roles_spec.rb b/spec/requests/admin/roles_spec.rb index 785da5a0ffdf5a..21853eb203ecf4 100644 --- a/spec/requests/admin/roles_spec.rb +++ b/spec/requests/admin/roles_spec.rb @@ -3,14 +3,142 @@ require 'rails_helper' RSpec.describe 'Admin Roles' do - describe 'POST /admin/roles' do + context 'when signed in as lower permissions user' do + let(:user_role) { Fabricate(:user_role, permissions: UserRole::Flags::NONE) } + + before { sign_in Fabricate(:user, role: user_role) } + + describe 'GET /admin/roles' do + it 'returns http forbidden' do + get admin_roles_path + + expect(response) + .to have_http_status(403) + end + end + + describe 'GET /admin/roles/new' do + it 'returns http forbidden' do + get new_admin_role_path + + expect(response) + .to have_http_status(403) + end + end + + describe 'GET /admin/roles/:id/edit' do + let(:role) { Fabricate(:user_role) } + + it 'returns http forbidden' do + get edit_admin_role_path(role) + + expect(response) + .to have_http_status(403) + end + end + + describe 'PUT /admin/roles/:id' do + let(:role) { Fabricate(:user_role) } + + it 'returns http forbidden' do + put admin_role_path(role) + + expect(response) + .to have_http_status(403) + end + end + + describe 'DELETE /admin/roles/:id' do + let(:role) { Fabricate(:user_role) } + + it 'returns http forbidden' do + delete admin_role_path(role) + + expect(response) + .to have_http_status(403) + end + end + end + + context 'when user has permissions to manage roles' do + let(:user_role) { Fabricate(:user_role, permissions: UserRole::FLAGS[:manage_users]) } + + before { sign_in Fabricate(:user, role: user_role) } + + context 'when target role permission outranks user' do + let(:role) { Fabricate(:user_role, position: user_role.position + 1) } + + describe 'GET /admin/roles/:id/edit' do + it 'returns http forbidden' do + get edit_admin_role_path(role) + + expect(response) + .to have_http_status(403) + end + end + + describe 'PUT /admin/roles/:id' do + it 'returns http forbidden' do + put admin_role_path(role) + + expect(response) + .to have_http_status(403) + end + end + + describe 'DELETE /admin/roles/:id' do + it 'returns http forbidden' do + delete admin_role_path(role) + + expect(response) + .to have_http_status(403) + end + end + end + end + + context 'when attempting to add permissions the user does not have' do + let(:user_role) { Fabricate(:user_role, permissions: UserRole::FLAGS[:manage_roles], position: 5) } + + before { sign_in Fabricate(:user, role: user_role) } + + describe 'POST /admin/roles' do + subject { post admin_roles_path, params: { user_role: { name: 'Bar', position: 2, permissions_as_keys: %w(manage_roles manage_users manage_reports) } } } + + it 'does not create role' do + expect { subject } + .to_not change(UserRole, :count) + + expect(response.body) + .to include(I18n.t('admin.roles.add_new')) + end + end + + describe 'PUT /admin/roles/:id' do + subject { put admin_role_path(role), params: { user_role: { position: 2, permissions_as_keys: %w(manage_roles manage_users manage_reports) } } } + + let(:role) { Fabricate(:user_role, name: 'Bar') } + + it 'does not create role' do + expect { subject } + .to_not(change { role.reload.permissions }) + + expect(response.parsed_body.title) + .to match(I18n.t('admin.roles.edit', name: 'Bar')) + end + end + end + + context 'when signed in as admin' do before { sign_in Fabricate(:admin_user) } - it 'gracefully handles invalid nested params' do - post admin_roles_path(user_role: 'invalid') + describe 'POST /admin/roles' do + it 'gracefully handles invalid nested params' do + post admin_roles_path(user_role: 'invalid') - expect(response) - .to have_http_status(400) + expect(response) + .to have_http_status(400) + end end end end diff --git a/spec/requests/settings/two_factor_authentication/confirmations_spec.rb b/spec/requests/settings/two_factor_authentication/confirmations_spec.rb index bf443a5e62fe00..532fbe61ea7fd3 100644 --- a/spec/requests/settings/two_factor_authentication/confirmations_spec.rb +++ b/spec/requests/settings/two_factor_authentication/confirmations_spec.rb @@ -16,4 +16,20 @@ .to have_http_status(400) end end + + context 'when not signed in' do + it 'redirects on POST to create' do + post settings_two_factor_authentication_confirmation_path(form_two_factor_confirmation: { otp_attempt: '123456' }) + + expect(response) + .to redirect_to(new_user_session_path) + end + + it 'redirects on GET to new' do + get new_settings_two_factor_authentication_confirmation_path + + expect(response) + .to redirect_to(new_user_session_path) + end + end end diff --git a/spec/system/admin/roles_spec.rb b/spec/system/admin/roles_spec.rb new file mode 100644 index 00000000000000..2a82d80b71574c --- /dev/null +++ b/spec/system/admin/roles_spec.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Admin::Roles' do + context 'when user has administrator permissions' do + let(:user_role) { Fabricate(:user_role, permissions: UserRole::FLAGS[:administrator], position: 10) } + + before { sign_in Fabricate(:user, role: user_role) } + + it 'creates new user role' do + visit new_admin_role_path + + fill_in 'user_role_name', with: 'Baz' + fill_in 'user_role_position', with: '1' + check 'user_role_permissions_as_keys_manage_reports' + check 'user_role_permissions_as_keys_manage_roles' + + expect { click_on I18n.t('admin.roles.add_new') } + .to change(UserRole, :count) + expect(page) + .to have_title(I18n.t('admin.roles.title')) + end + end + + context 'when user has permissions to manage roles' do + let(:user_role) { Fabricate(:user_role, permissions: UserRole::FLAGS[:manage_roles], position: 10) } + + before { sign_in Fabricate(:user, role: user_role) } + + it 'Creates user roles' do + visit admin_roles_path + expect(page) + .to have_title(I18n.t('admin.roles.title')) + + click_on I18n.t('admin.roles.add_new') + expect(page) + .to have_title(I18n.t('admin.roles.add_new')) + + # Position too high + fill_in 'user_role_name', with: 'Baz' + fill_in 'user_role_position', with: '100' + expect { click_on I18n.t('admin.roles.add_new') } + .to_not change(UserRole, :count) + expect(page) + .to have_content(I18n.t('activerecord.errors.models.user_role.attributes.position.elevated')) + + # Valid submission + fill_in 'user_role_name', with: 'Baz' + fill_in 'user_role_position', with: '5' # Lower than user + check 'user_role_permissions_as_keys_manage_roles' # User has permission + expect { click_on I18n.t('admin.roles.add_new') } + .to change(UserRole, :count) + expect(page) + .to have_title(I18n.t('admin.roles.title')) + end + + it 'Manages existing user roles' do + role = Fabricate :user_role, name: 'Baz' + + visit edit_admin_role_path(role) + expect(page) + .to have_title(I18n.t('admin.roles.edit', name: 'Baz')) + + # Update role attribute + fill_in 'user_role_position', with: '5' # Lower than user + expect { click_on submit_button } + .to(change { role.reload.position }) + + # Destroy the role + visit edit_admin_role_path(role) + expect { click_on I18n.t('admin.roles.delete') } + .to change(UserRole, :count).by(-1) + expect(page) + .to have_title(I18n.t('admin.roles.title')) + end + end +end diff --git a/streaming/index.js b/streaming/index.js index 9c2b671c65e103..d53f4ffcd4fe6c 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -659,7 +659,7 @@ const startServer = async () => { // filtering of statuses: // Filter based on language: - if (Array.isArray(req.chosenLanguages) && payload.language !== null && req.chosenLanguages.indexOf(payload.language) === -1) { + if (Array.isArray(req.chosenLanguages) && req.chosenLanguages.indexOf(payload.language) === -1) { log.debug(`Message ${payload.id} filtered by language (${payload.language})`); return; } diff --git a/yarn.lock b/yarn.lock index ba4781e3ce6700..fee6395a072c57 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14149,13 +14149,13 @@ __metadata: linkType: hard "postcss@npm:^8.2.15, postcss@npm:^8.4.24, postcss@npm:^8.4.49": - version: 8.5.2 - resolution: "postcss@npm:8.5.2" + version: 8.5.3 + resolution: "postcss@npm:8.5.3" dependencies: nanoid: "npm:^3.3.8" picocolors: "npm:^1.1.1" source-map-js: "npm:^1.2.1" - checksum: 10c0/3044d49bc725029ab62292e8bf9849741251b95f3b754e191bf8b4025414d40ec3b4ac05c5a563d4b50060b5c8e96683eb4d783d8d8fa3867eb7b763cbe66127 + checksum: 10c0/b75510d7b28c3ab728c8733dd01538314a18c52af426f199a3c9177e63eb08602a3938bfb66b62dc01350b9aed62087eabbf229af97a1659eb8d3513cec823b3 languageName: node linkType: hard @@ -17773,11 +17773,11 @@ __metadata: linkType: hard "uuid@npm:^11.0.0": - version: 11.0.5 - resolution: "uuid@npm:11.0.5" + version: 11.1.0 + resolution: "uuid@npm:11.1.0" bin: uuid: dist/esm/bin/uuid - checksum: 10c0/6f59f0c605e02c14515401084ca124b9cb462b4dcac866916a49862bcf831874508a308588c23a7718269226ad11a92da29b39d761ad2b86e736623e3a33b6e7 + checksum: 10c0/34aa51b9874ae398c2b799c88a127701408cd581ee89ec3baa53509dd8728cbb25826f2a038f9465f8b7be446f0fbf11558862965b18d21c993684297628d4d3 languageName: node linkType: hard @@ -18646,8 +18646,8 @@ __metadata: linkType: hard "ws@npm:^8.11.0, ws@npm:^8.12.1, ws@npm:^8.18.0": - version: 8.18.0 - resolution: "ws@npm:8.18.0" + version: 8.18.1 + resolution: "ws@npm:8.18.1" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -18656,7 +18656,7 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 10c0/25eb33aff17edcb90721ed6b0eb250976328533ad3cd1a28a274bd263682e7296a6591ff1436d6cbc50fa67463158b062f9d1122013b361cec99a05f84680e06 + checksum: 10c0/e498965d6938c63058c4310ffb6967f07d4fa06789d3364829028af380d299fe05762961742971c764973dce3d1f6a2633fe8b2d9410c9b52e534b4b882a99fa languageName: node linkType: hard