mirror of
https://github.com/opf/openproject.git
synced 2026-06-13 19:20:00 +00:00
396 lines
11 KiB
Ruby
396 lines
11 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
#-- copyright
|
|
# OpenProject is an open source project management software.
|
|
# Copyright (C) the OpenProject GmbH
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License version 3.
|
|
#
|
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
|
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
# Copyright (C) 2010-2013 the ChiliProject Team
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# See COPYRIGHT and LICENSE files for more details.
|
|
#++
|
|
|
|
require "spec_helper"
|
|
|
|
RSpec.describe MyController do
|
|
let(:user) { create(:user) }
|
|
|
|
before do
|
|
login_as(user)
|
|
end
|
|
|
|
describe "password change" do
|
|
describe "#password" do
|
|
before do
|
|
get :password
|
|
end
|
|
|
|
it "renders the password template" do
|
|
assert_template "password"
|
|
expect(response).to have_http_status(:success)
|
|
end
|
|
end
|
|
|
|
describe "with disabled password login" do
|
|
before do
|
|
allow(OpenProject::Configuration).to receive(:disable_password_login?).and_return(true)
|
|
post :change_password
|
|
end
|
|
|
|
it "is not found" do
|
|
expect(response.status).to eq 404
|
|
end
|
|
end
|
|
|
|
describe "with wrong confirmation" do
|
|
before do
|
|
post :change_password,
|
|
params: {
|
|
password: "adminADMIN!",
|
|
new_password: "adminADMIN!New",
|
|
new_password_confirmation: "adminADMIN!Other"
|
|
}
|
|
end
|
|
|
|
it "shows an error message" do
|
|
expect(response).to have_http_status :unprocessable_entity
|
|
assert_template "password"
|
|
expect(user.errors.attribute_names).to eq([:password_confirmation])
|
|
expect(user.errors.map(&:message).flatten)
|
|
.to contain_exactly("Password confirmation does not match password.")
|
|
end
|
|
end
|
|
|
|
describe "with wrong password" do
|
|
render_views
|
|
before do
|
|
@current_password = user.current_password.id
|
|
post :change_password,
|
|
params: {
|
|
password: "wrongpassword",
|
|
new_password: "adminADMIN!New",
|
|
new_password_confirmation: "adminADMIN!New"
|
|
}
|
|
end
|
|
|
|
it "shows an error message" do
|
|
expect(response).to have_http_status :unprocessable_entity
|
|
assert_template "password"
|
|
expect(flash[:error]).to eq("Wrong password")
|
|
end
|
|
|
|
it "does not change the password" do
|
|
expect(user.current_password.id).to eq(@current_password)
|
|
end
|
|
end
|
|
|
|
describe "with good password and good confirmation" do
|
|
before do
|
|
post :change_password,
|
|
params: {
|
|
password: "adminADMIN!",
|
|
new_password: "adminADMIN!New",
|
|
new_password_confirmation: "adminADMIN!New"
|
|
}
|
|
end
|
|
|
|
it "redirects to the my password page" do
|
|
expect(response).to redirect_to("/my/password")
|
|
end
|
|
|
|
it "allows the user to login with the new password" do
|
|
assert User.try_to_login(user.login, "adminADMIN!New")
|
|
end
|
|
end
|
|
|
|
describe "with brute force protection",
|
|
with_settings: { brute_force_block_minutes: 30, brute_force_block_after_failed_logins: 20 } do
|
|
describe "blocks password change attempts after too many failures" do
|
|
before do
|
|
user.update_columns(
|
|
failed_login_count: 20,
|
|
last_failed_login_on: 1.minute.ago
|
|
)
|
|
|
|
post :change_password,
|
|
params: {
|
|
password: "adminADMIN!",
|
|
new_password: "adminADMIN!New",
|
|
new_password_confirmation: "adminADMIN!New"
|
|
}
|
|
end
|
|
|
|
it "blocks the attempt even with correct password" do
|
|
expect(response).to have_http_status :unprocessable_entity
|
|
end
|
|
|
|
it "does not change the password" do
|
|
user.reload
|
|
expect(user.check_password?("adminADMIN!")).to be true
|
|
expect(user.check_password?("adminADMIN!New")).to be false
|
|
end
|
|
end
|
|
|
|
describe "logs failed password attempts" do
|
|
before do
|
|
user.update_columns(
|
|
failed_login_count: 0,
|
|
last_failed_login_on: nil
|
|
)
|
|
|
|
post :change_password,
|
|
params: {
|
|
password: "WrongPassword!",
|
|
new_password: "adminADMIN!New",
|
|
new_password_confirmation: "adminADMIN!New"
|
|
}
|
|
end
|
|
|
|
it "increments failed login count" do
|
|
user.reload
|
|
expect(user.failed_login_count).to eq(1)
|
|
end
|
|
|
|
it "updates last failed login timestamp" do
|
|
user.reload
|
|
expect(user.last_failed_login_on).to be_within(1.second).of(Time.zone.now)
|
|
end
|
|
end
|
|
|
|
describe "resets failed login count on successful password change" do
|
|
before do
|
|
user.update_columns(
|
|
failed_login_count: 5,
|
|
last_failed_login_on: 1.minute.ago
|
|
)
|
|
|
|
post :change_password,
|
|
params: {
|
|
password: "adminADMIN!",
|
|
new_password: "adminADMIN!New",
|
|
new_password_confirmation: "adminADMIN!New"
|
|
}
|
|
end
|
|
|
|
it "resets the failed login count to zero" do
|
|
user.reload
|
|
expect(user.failed_login_count).to eq(0)
|
|
end
|
|
|
|
it "changes the password successfully" do
|
|
user.reload
|
|
expect(user.check_password?("adminADMIN!New")).to be true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "account" do
|
|
let(:custom_field) { create(:user_custom_field, :text) }
|
|
|
|
before do
|
|
custom_field
|
|
as_logged_in_user user do
|
|
get :account
|
|
end
|
|
end
|
|
|
|
it "responds with success" do
|
|
expect(response).to be_successful
|
|
end
|
|
|
|
it "renders the account template" do
|
|
expect(response).to render_template "account"
|
|
end
|
|
|
|
it "assigns @user" do
|
|
expect(assigns(:user)).to eq(user)
|
|
end
|
|
|
|
context "with render_views" do
|
|
render_views
|
|
it "renders editable custom fields" do
|
|
expect(response.body).to have_content(custom_field.name)
|
|
end
|
|
|
|
it "renders the 'Change password' menu entry" do
|
|
expect(response.body).to have_css("#menu-sidebar li a", text: "Change password")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "locale" do
|
|
it "renders the locale template" do
|
|
as_logged_in_user user do
|
|
get :locale
|
|
end
|
|
|
|
expect(response).to be_successful
|
|
expect(response).to render_template "locale"
|
|
end
|
|
end
|
|
|
|
describe "settings" do
|
|
describe "PATCH" do
|
|
let(:language) { "en" }
|
|
let(:params) do
|
|
{
|
|
user: { language: },
|
|
pref: { auto_hide_popups: 0 }
|
|
}
|
|
end
|
|
|
|
before do
|
|
as_logged_in_user user do
|
|
user.pref.comments_sorting = "desc"
|
|
user.pref.auto_hide_popups = true
|
|
|
|
patch :update_settings, params:
|
|
end
|
|
end
|
|
|
|
it "updates the settings appropriately", :aggregate_failures do
|
|
expect(assigns(:user).language).to eq language
|
|
expect(assigns(:user).pref.comments_sorting).to eql "desc"
|
|
expect(assigns(:user).pref.auto_hide_popups?).to be_falsey
|
|
|
|
expect(request.path).to eq(my_settings_path)
|
|
expect(flash[:notice]).to eql I18n.t(:notice_account_updated)
|
|
end
|
|
|
|
context "when user is invalid" do
|
|
let(:user) do
|
|
create(:user).tap do |u|
|
|
u.update_column(:mail, "something invalid")
|
|
end
|
|
end
|
|
|
|
it "shows a flash error" do
|
|
expect(flash[:error]).to include "Email is not a valid email address."
|
|
expect(request.path).to eq(my_settings_path)
|
|
end
|
|
end
|
|
|
|
context "when changing language" do
|
|
let(:language) { "de" }
|
|
|
|
it "shows a flash message translated in the selected language" do
|
|
expect(assigns(:user).language).to eq(language)
|
|
expect(flash[:notice]).to eq(I18n.t(:notice_account_updated, locale: language))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "changing changing mail" do
|
|
let!(:recovery_token) { create(:recovery_token, user:) }
|
|
let!(:plain_session) { create(:user_session, user:, session_id: "internal_foobar") }
|
|
let!(:user_session) { Sessions::UserSession.find_by(session_id: "internal_foobar") }
|
|
|
|
let(:params) do
|
|
{ user: { mail: "foo@example.org" } }
|
|
end
|
|
|
|
it "clears other sessions and removes tokens" do
|
|
as_logged_in_user user do
|
|
patch :update_settings, params:
|
|
end
|
|
|
|
expect(flash[:info]).to include(I18n.t(:notice_account_updated))
|
|
expect(flash[:info]).to include(I18n.t(:notice_account_other_session_expired))
|
|
|
|
expect(Token::Recovery.where(user:)).to be_empty
|
|
expect { user_session.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
|
end
|
|
end
|
|
|
|
describe "interface:auto_hide_popups" do
|
|
context "with render_views" do
|
|
before do
|
|
as_logged_in_user user do
|
|
get :interface
|
|
end
|
|
end
|
|
|
|
render_views
|
|
it "renders auto hide popups checkbox" do
|
|
expect(response.body).to have_css("form #auto_hide_popups")
|
|
end
|
|
end
|
|
|
|
context "PATCH" do
|
|
before do
|
|
as_logged_in_user user do
|
|
user.pref.auto_hide_popups = false
|
|
|
|
patch :update_settings, params: { user: { language: "en" } }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "account with disabled password login" do
|
|
before do
|
|
allow(OpenProject::Configuration).to receive(:disable_password_login?).and_return(true)
|
|
as_logged_in_user user do
|
|
get :account
|
|
end
|
|
end
|
|
|
|
render_views
|
|
|
|
it "does not render 'Change password' menu entry" do
|
|
expect(response.body).to have_no_css("#menu-sidebar li a", text: "Change password")
|
|
end
|
|
end
|
|
|
|
describe "#working_times" do
|
|
let!(:user_working_hours) { create(:user_working_hours, valid_from: 1.week.ago, user:) }
|
|
|
|
subject { get :working_hours }
|
|
|
|
context "with feature enabled", with_flag: { user_working_times: true } do
|
|
it "responds with success" do
|
|
subject
|
|
expect(response).to be_successful
|
|
end
|
|
|
|
it "renders the working_hours template" do
|
|
subject
|
|
expect(response).to render_template "working_hours"
|
|
end
|
|
|
|
it "assigns @current_working_hours and @past_working_hours" do
|
|
subject
|
|
expect(assigns(:current_working_hours)).to eq(user_working_hours)
|
|
expect(assigns(:past_working_hours)).to eq([user_working_hours])
|
|
end
|
|
end
|
|
|
|
context "with feature disabled", with_flag: { user_working_times: false } do
|
|
it "responds with forbidden" do
|
|
subject
|
|
expect(response).to have_http_status(:forbidden)
|
|
end
|
|
end
|
|
end
|
|
end
|