From 6376eaa64ddd320961ee31d7e571ed783d5a492e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Verg=C3=A9s?= Date: Fri, 26 Aug 2022 16:28:43 +0200 Subject: [PATCH] fix leaking emails on admin user search controller --- .../decidim/admin/organization_controller.rb | 6 +- .../organizations_contoller_spec.rb | 115 ----------- .../organizations_controller_spec.rb | 181 ++++++++++++++++++ .../app/models/decidim/user_base_entity.rb | 1 + 4 files changed, 185 insertions(+), 118 deletions(-) delete mode 100644 decidim-admin/spec/controllers/organizations_contoller_spec.rb create mode 100644 decidim-admin/spec/controllers/organizations_controller_spec.rb diff --git a/decidim-admin/app/controllers/decidim/admin/organization_controller.rb b/decidim-admin/app/controllers/decidim/admin/organization_controller.rb index 14be01e7c8230..e9ca3c2e15241 100644 --- a/decidim-admin/app/controllers/decidim/admin/organization_controller.rb +++ b/decidim-admin/app/controllers/decidim/admin/organization_controller.rb @@ -30,11 +30,11 @@ def update end def users - search(current_organization.users) + search(current_organization.users.available) end def user_entities - search(current_organization.user_entities) + search(current_organization.user_entities.available) end private @@ -51,7 +51,7 @@ def search(relation) query.where("email ILIKE ?", "%#{term}%") ) end - render json: query.all.collect { |u| { value: u.id, label: "#{u.name} (@#{u.nickname}) #{u.email}" } } + render json: query.all.collect { |u| { value: u.id, label: "#{u.name} (@#{u.nickname})" } } else render json: [] end diff --git a/decidim-admin/spec/controllers/organizations_contoller_spec.rb b/decidim-admin/spec/controllers/organizations_contoller_spec.rb deleted file mode 100644 index 3e782cf9b8ba1..0000000000000 --- a/decidim-admin/spec/controllers/organizations_contoller_spec.rb +++ /dev/null @@ -1,115 +0,0 @@ -# frozen_string_literal: true - -require "spec_helper" - -module Decidim - module Admin - describe OrganizationController, type: :controller do - routes { Decidim::Admin::Engine.routes } - - let(:organization) { create :organization } - let(:current_user) { create(:user, :admin, :confirmed, organization: organization) } - - before do - request.env["decidim.current_organization"] = organization - sign_in current_user, scope: :user - end - - describe "GET users and user groups in json format" do - let!(:user) { create(:user, name: "Daisy Miller", nickname: "daisy_m", organization: organization, email: "d.mail@example.org") } - let!(:other_user) { create(:user, name: "Daisy O'connor", nickname: "daisy_o", email: "d.mail.o@example.org") } - let!(:user_group) do - create( - :user_group, - :verified, - name: "Daisy Organization", - nickname: "daisy_org", - email: "d.mail.org@example.org", - users: [user], - organization: organization - ) - end - let(:parsed_response) { JSON.parse(response.body).map(&:symbolize_keys) } - - context "when searching by name" do - it "returns the id, name, email and nickname for filtered users and user groups" do - get :user_entities, format: :json, params: { term: "daisy" } - expect(parsed_response).to include({ value: user.id, label: "#{user.name} (@#{user.nickname}) #{user.email}" }) - expect(parsed_response).to include({ value: user_group.id, label: "#{user_group.name} (@#{user_group.nickname}) #{user_group.email}" }) - expect(parsed_response).not_to include({ value: other_user.id, label: "#{other_user.name} (@#{other_user.nickname}) #{other_user.email}" }) - end - end - - context "when searching by nickname" do - it "returns the id, name, email and nickname for filtered users and user groups" do - get :user_entities, format: :json, params: { term: "@daisy" } - expect(parsed_response).to include({ value: user.id, label: "#{user.name} (@#{user.nickname}) #{user.email}" }) - expect(parsed_response).to include({ value: user_group.id, label: "#{user_group.name} (@#{user_group.nickname}) #{user_group.email}" }) - expect(parsed_response).not_to include({ value: other_user.id, label: "#{other_user.name} (@#{other_user.nickname}) #{other_user.email}" }) - end - end - - context "when searching by email" do - it "returns the id, name, email and nickname for filtered users and user groups" do - get :user_entities, format: :json, params: { term: "d.mail" } - expect(parsed_response).to include({ value: user.id, label: "#{user.name} (@#{user.nickname}) #{user.email}" }) - expect(parsed_response).to include({ value: user_group.id, label: "#{user_group.name} (@#{user_group.nickname}) #{user_group.email}" }) - expect(parsed_response).not_to include({ value: other_user.id, label: "#{other_user.name} (@#{other_user.nickname}) #{other_user.email}" }) - end - end - end - - describe "GET users in json format" do - let!(:user) { create(:user, name: "Daisy Miller", nickname: "daisy_m", organization: organization) } - let!(:other_user) { create(:user, name: "Daisy O'connor", nickname: "daisy_o") } - let!(:user_group) do - create( - :user_group, - :verified, - name: "Daisy Organization", - nickname: "daysy_org", - users: [user], - organization: organization - ) - end - - let(:parsed_response) { JSON.parse(response.body).map(&:symbolize_keys) } - - context "when no search term is provided" do - it "returns an empty result set" do - get :users, format: :json, params: {} - expect(parsed_response).to eq([]) - end - end - - context "when there are no results" do - it "returns an empty json array" do - get :users, format: :json, params: { term: "#0" } - expect(parsed_response).to eq([]) - end - end - - context "when searching by name" do - it "returns the id, name, email and nickname for filtered users" do - get :users, format: :json, params: { term: "daisy" } - expect(parsed_response).to eq([{ value: user.id, label: "#{user.name} (@#{user.nickname}) #{user.email}" }]) - end - end - - context "when searching by nickname" do - it "returns the id, name, email and nickname for filtered users" do - get :users, format: :json, params: { term: "@daisy" } - expect(parsed_response).to eq([{ value: user.id, label: "#{user.name} (@#{user.nickname}) #{user.email}" }]) - end - end - - context "when searching by email" do - it "returns the id, name, email and nickname for filtered users" do - get :users, format: :json, params: { term: user.email } - expect(parsed_response).to eq([{ value: user.id, label: "#{user.name} (@#{user.nickname}) #{user.email}" }]) - end - end - end - end - end -end diff --git a/decidim-admin/spec/controllers/organizations_controller_spec.rb b/decidim-admin/spec/controllers/organizations_controller_spec.rb new file mode 100644 index 0000000000000..a27c0a3b55d52 --- /dev/null +++ b/decidim-admin/spec/controllers/organizations_controller_spec.rb @@ -0,0 +1,181 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim + module Admin + describe OrganizationController, type: :controller do + routes { Decidim::Admin::Engine.routes } + + let(:organization) { create :organization } + let(:current_user) { create(:user, :admin, :confirmed, organization: organization) } + + before do + request.env["decidim.current_organization"] = organization + sign_in current_user, scope: :user + end + + describe "GET users and user groups in json format" do + let!(:user) { create(:user, name: "Daisy Miller", nickname: "daisy_m", organization: organization, email: "d.mail@example.org") } + let!(:blocked_user) { create(:user, :blocked, name: "Daisy Blocked", nickname: "daisy_b", organization: organization, email: "d.mail.b@example.org") } + let!(:managed_user) { create(:user, :managed, name: "Daisy Managed", nickname: "daisy_g", organization: organization, email: "d.mail.g@example.org") } + let!(:deleted_user) { create(:user, :deleted, name: "Daisy Deleted", nickname: "daisy_d", organization: organization, email: "d.mail.d@example.org") } + let!(:other_user) { create(:user, name: "Daisy O'connor", nickname: "daisy_o", email: "d.mail.o@example.org") } + let!(:user_group) do + create( + :user_group, + :verified, + name: "Daisy Organization", + nickname: "daisy_org", + email: "d.mail.org@example.org", + users: [user], + organization: organization + ) + end + let(:parsed_response) { JSON.parse(response.body).map(&:symbolize_keys) } + + context "when searching by name" do + it "returns the id, name and nickname for filtered users and user groups" do + get :user_entities, format: :json, params: { term: "daisy" } + expect(parsed_response).to include({ value: user.id, label: "#{user.name} (@#{user.nickname})" }) + expect(parsed_response).to include({ value: user_group.id, label: "#{user_group.name} (@#{user_group.nickname})" }) + expect(parsed_response).not_to include({ value: other_user.id, label: "#{other_user.name} (@#{other_user.nickname})" }) + expect(parsed_response).not_to include({ value: blocked_user.id, label: "#{blocked_user.name} (@#{blocked_user.nickname})" }) + expect(parsed_response).not_to include({ value: deleted_user.id, label: "#{deleted_user.name} (@#{deleted_user.nickname})" }) + expect(parsed_response).not_to include({ value: managed_user.id, label: "#{managed_user.name} (@#{managed_user.nickname})" }) + end + end + + context "when searching by nickname" do + it "returns the id, name and nickname for filtered users and user groups" do + get :user_entities, format: :json, params: { term: "@daisy" } + expect(parsed_response).to include({ value: user.id, label: "#{user.name} (@#{user.nickname})" }) + expect(parsed_response).to include({ value: user_group.id, label: "#{user_group.name} (@#{user_group.nickname})" }) + expect(parsed_response).not_to include({ value: other_user.id, label: "#{other_user.name} (@#{other_user.nickname})" }) + expect(parsed_response).not_to include({ value: blocked_user.id, label: "#{blocked_user.name} (@#{blocked_user.nickname})" }) + expect(parsed_response).not_to include({ value: deleted_user.id, label: "#{deleted_user.name} (@#{deleted_user.nickname})" }) + expect(parsed_response).not_to include({ value: managed_user.id, label: "#{managed_user.name} (@#{managed_user.nickname})" }) + end + end + + context "when searching by email" do + it "returns the id, name and nickname for filtered users and user groups" do + get :user_entities, format: :json, params: { term: "d.mail" } + expect(parsed_response).to include({ value: user.id, label: "#{user.name} (@#{user.nickname})" }) + expect(parsed_response).to include({ value: user_group.id, label: "#{user_group.name} (@#{user_group.nickname})" }) + expect(parsed_response).not_to include({ value: other_user.id, label: "#{other_user.name} (@#{other_user.nickname})" }) + expect(parsed_response).not_to include({ value: blocked_user.id, label: "#{blocked_user.name} (@#{blocked_user.nickname})" }) + expect(parsed_response).not_to include({ value: deleted_user.id, label: "#{deleted_user.name} (@#{deleted_user.nickname})" }) + expect(parsed_response).not_to include({ value: managed_user.id, label: "#{managed_user.name} (@#{managed_user.nickname})" }) + end + end + + context "when user is blocked" do + let!(:user) { create(:user, :blocked, name: "Daisy Miller", nickname: "daisy_m", organization: organization) } + + it "returns an empty json array" do + get :users, format: :json, params: { term: "daisy" } + expect(parsed_response).to eq([]) + end + end + + context "when user is managed" do + let!(:user) { create(:user, :managed, name: "Daisy Miller", nickname: "daisy_m", organization: organization) } + + it "returns an empty json array" do + get :users, format: :json, params: { term: "daisy" } + expect(parsed_response).to eq([]) + end + end + + context "when user is deleted" do + let!(:user) { create(:user, :deleted, name: "Daisy Miller", nickname: "daisy_m", organization: organization) } + + it "returns an empty json array" do + get :users, format: :json, params: { term: "daisy" } + expect(parsed_response).to eq([]) + end + end + end + + describe "GET users in json format" do + let!(:user) { create(:user, name: "Daisy Miller", nickname: "daisy_m", organization: organization) } + let!(:other_user) { create(:user, name: "Daisy O'connor", nickname: "daisy_o") } + let!(:user_group) do + create( + :user_group, + :verified, + name: "Daisy Organization", + nickname: "daysy_org", + users: [user], + organization: organization + ) + end + + let(:parsed_response) { JSON.parse(response.body).map(&:symbolize_keys) } + + context "when no search term is provided" do + it "returns an empty result set" do + get :users, format: :json, params: {} + expect(parsed_response).to eq([]) + end + end + + context "when there are no results" do + it "returns an empty json array" do + get :users, format: :json, params: { term: "#0" } + expect(parsed_response).to eq([]) + end + end + + context "when searching by name" do + it "returns the id, name and nickname for filtered users" do + get :users, format: :json, params: { term: "daisy" } + expect(parsed_response).to eq([{ value: user.id, label: "#{user.name} (@#{user.nickname})" }]) + end + end + + context "when searching by nickname" do + it "returns the id, name and nickname for filtered users" do + get :users, format: :json, params: { term: "@daisy" } + expect(parsed_response).to eq([{ value: user.id, label: "#{user.name} (@#{user.nickname})" }]) + end + end + + context "when searching by email" do + it "returns the id, name and nickname for filtered users" do + get :users, format: :json, params: { term: user.email } + expect(parsed_response).to eq([{ value: user.id, label: "#{user.name} (@#{user.nickname})" }]) + end + end + + context "when user is blocked" do + let!(:user) { create(:user, :blocked, name: "Daisy Miller", nickname: "daisy_m", organization: organization) } + + it "returns an empty json array" do + get :users, format: :json, params: { term: "daisy" } + expect(parsed_response).to eq([]) + end + end + + context "when user is managed" do + let!(:user) { create(:user, :managed, name: "Daisy Miller", nickname: "daisy_m", organization: organization) } + + it "returns an empty json array" do + get :users, format: :json, params: { term: "daisy" } + expect(parsed_response).to eq([]) + end + end + + context "when user is deleted" do + let!(:user) { create(:user, :deleted, name: "Daisy Miller", nickname: "daisy_m", organization: organization) } + + it "returns an empty json array" do + get :users, format: :json, params: { term: "daisy" } + expect(parsed_response).to eq([]) + end + end + end + end + end +end diff --git a/decidim-core/app/models/decidim/user_base_entity.rb b/decidim-core/app/models/decidim/user_base_entity.rb index 3a18a99b5ac4f..f6ae9e7575419 100644 --- a/decidim-core/app/models/decidim/user_base_entity.rb +++ b/decidim-core/app/models/decidim/user_base_entity.rb @@ -30,6 +30,7 @@ class UserBaseEntity < ApplicationRecord scope :blocked, -> { where(blocked: true) } scope :not_blocked, -> { where(blocked: false) } + scope :available, -> { where(deleted_at: nil, blocked: false, managed: false) } # Public: Returns a collection with all the public entities this user is following. #