Skip to content

Commit f8ae64f

Browse files
committed
Use correctly scoped loading for sessions
1 parent 3d3d7d0 commit f8ae64f

2 files changed

Lines changed: 147 additions & 3 deletions

File tree

app/controllers/my/sessions_controller.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ class SessionsController < ::ApplicationController
3434
no_authorization_required! :index,
3535
:destroy
3636

37-
self._model_object = ::Sessions::UserSession
38-
39-
before_action :find_model_object, only: %i(show destroy)
37+
before_action :load_session, only: %i(destroy)
4038
before_action :prevent_current_session_deletion, only: %i(destroy)
4139

4240
layout "my"
@@ -67,6 +65,10 @@ def destroy
6765

6866
private
6967

68+
def load_session
69+
@session = ::Sessions::UserSession.for_user(current_user).find(params[:id])
70+
end
71+
7072
def prevent_current_session_deletion
7173
if @session.current?(session)
7274
render_400 message: I18n.t("users.sessions.may_not_delete_current")
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# frozen_string_literal: true
2+
3+
#-- copyright
4+
# OpenProject is an open source project management software.
5+
# Copyright (C) the OpenProject GmbH
6+
#
7+
# This program is free software; you can redistribute it and/or
8+
# modify it under the terms of the GNU General Public License version 3.
9+
#
10+
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
11+
# Copyright (C) 2006-2013 Jean-Philippe Lang
12+
# Copyright (C) 2010-2013 the ChiliProject Team
13+
#
14+
# This program is free software; you can redistribute it and/or
15+
# modify it under the terms of the GNU General Public License
16+
# as published by the Free Software Foundation; either version 2
17+
# of the License, or (at your option) any later version.
18+
#
19+
# This program is distributed in the hope that it will be useful,
20+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
21+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+
# GNU General Public License for more details.
23+
#
24+
# You should have received a copy of the GNU General Public License
25+
# along with this program; if not, write to the Free Software
26+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27+
#
28+
# See COPYRIGHT and LICENSE files for more details.
29+
#++
30+
31+
require "spec_helper"
32+
33+
RSpec.describe My::SessionsController do
34+
let(:user) { create(:user) }
35+
36+
before do
37+
login_as(user)
38+
end
39+
40+
describe "#index" do
41+
let!(:autologin_token) do
42+
create(:autologin_token, user:, expires_on: 1.year.from_now)
43+
end
44+
45+
let!(:expired_autologin_token) do
46+
create(:autologin_token, user:, expires_on: 1.year.ago)
47+
end
48+
49+
let!(:auto_login_token_in_cookie) do
50+
create(:autologin_token, user:, expires_on: 1.year.from_now)
51+
end
52+
53+
let!(:auto_login_token_other_user) do
54+
create(:autologin_token, expires_on: 1.year.from_now)
55+
end
56+
57+
let!(:unmapped_session) do
58+
# As session models are readonly we need to create them with the factory and then manually refind them
59+
session = create(:user_session, user:)
60+
Sessions::UserSession.find_by(session_id: session.session_id)
61+
end
62+
63+
before do
64+
cookies[OpenProject::Configuration["autologin_cookie_name"]] = auto_login_token_in_cookie.plain_value
65+
end
66+
67+
it "assigns all variables the views expect" do
68+
get :index
69+
70+
expect(assigns(:autologin_tokens)).to contain_exactly(autologin_token, auto_login_token_in_cookie)
71+
expect(assigns(:unmapped_sessions)).to contain_exactly(unmapped_session)
72+
expect(assigns(:current_token)).to eq(auto_login_token_in_cookie)
73+
end
74+
75+
context "when no autologin token is in the cookie" do
76+
before do
77+
cookies.delete(OpenProject::Configuration["autologin_cookie_name"])
78+
end
79+
80+
it "does not assign a current_token" do
81+
get :index
82+
83+
expect(assigns(:current_token)).to be_nil
84+
end
85+
end
86+
end
87+
88+
describe "#destroy" do
89+
let(:session_owner) { user }
90+
let!(:session) do
91+
# As session models are readonly we need to create them with the factory and then manually refind them
92+
session = create(:user_session, user: session_owner)
93+
Sessions::UserSession.find_by(session_id: session.session_id)
94+
end
95+
96+
let(:is_current_session) { false }
97+
98+
before do
99+
# We do not want to mock any loading of the session itself as that is also a security relevant path.
100+
allow_any_instance_of(Sessions::UserSession) # rubocop:disable RSpec/AnyInstance
101+
.to receive(:current?)
102+
.and_return(is_current_session)
103+
end
104+
105+
context "when session is current session" do
106+
let(:is_current_session) { true }
107+
108+
it "prevents deletion of the current session" do
109+
delete :destroy, params: { id: session.id }
110+
111+
expect { session.reload }.not_to raise_error
112+
113+
expect(response).to have_http_status(:bad_request)
114+
end
115+
end
116+
117+
context "when session is not current session" do
118+
let(:is_current_session) { false }
119+
120+
it "allows deletion of other sessions" do
121+
delete :destroy, params: { id: session.id }
122+
123+
expect { session.reload }.to raise_error(ActiveRecord::RecordNotFound)
124+
125+
expect(response).to redirect_to(my_sessions_path)
126+
expect(flash[:notice]).to eq(I18n.t(:notice_successful_delete))
127+
end
128+
end
129+
130+
context "when the session belongs to another user" do
131+
let(:session_owner) { create(:user) }
132+
133+
it "responds with 404 Not Found" do
134+
delete :destroy, params: { id: session.id }
135+
136+
expect { session.reload }.not_to raise_error
137+
138+
expect(response).to have_http_status(:not_found)
139+
end
140+
end
141+
end
142+
end

0 commit comments

Comments
 (0)