Existing user session when requesting SSO session create endpoint will fail device check because of missing fingerprint param (which is required as soon as a user/session is present).
This commit is contained in:
parent
817cc72194
commit
d1ed72a071
5 changed files with 38 additions and 78 deletions
|
@ -143,10 +143,10 @@ module ApplicationController::Authenticates
|
||||||
User.lookup(login: login&.downcase)
|
User.lookup(login: login&.downcase)
|
||||||
end
|
end
|
||||||
|
|
||||||
raise Exceptions::NotAuthorized, 'no valid session' if !user
|
raise Exceptions::NotAuthorized, 'Missing SSO ENV REMOTE_USER' if !user
|
||||||
|
|
||||||
session.delete(:switched_from_user_id)
|
session.delete(:switched_from_user_id)
|
||||||
authentication_check_prerequesits(user, 'session', {})
|
authentication_check_prerequesits(user, 'SSO', {})
|
||||||
end
|
end
|
||||||
|
|
||||||
def authentication_check_prerequesits(user, auth_type, auth_param)
|
def authentication_check_prerequesits(user, auth_type, auth_param)
|
||||||
|
|
|
@ -17,6 +17,7 @@ module ApplicationController::HandlesDevices
|
||||||
return true if switched_from_user_id
|
return true if switched_from_user_id
|
||||||
return true if !user
|
return true if !user
|
||||||
return true if !user.permissions?('user_preferences.device')
|
return true if !user.permissions?('user_preferences.device')
|
||||||
|
return true if type == 'SSO'
|
||||||
|
|
||||||
time_to_check = true
|
time_to_check = true
|
||||||
user_device_updated_at = session[:user_device_updated_at]
|
user_device_updated_at = session[:user_device_updated_at]
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
class SessionsController < ApplicationController
|
class SessionsController < ApplicationController
|
||||||
prepend_before_action :authentication_check, only: %i[switch_to_user list delete]
|
prepend_before_action :authentication_check, only: %i[switch_to_user list delete]
|
||||||
skip_before_action :verify_csrf_token, only: %i[show destroy create_omniauth failure_omniauth]
|
skip_before_action :verify_csrf_token, only: %i[show destroy create_omniauth failure_omniauth]
|
||||||
|
skip_before_action :user_device_check, only: %i[create_sso]
|
||||||
|
|
||||||
# "Create" a login, aka "log the user in"
|
# "Create" a login, aka "log the user in"
|
||||||
def create
|
def create
|
||||||
|
@ -14,15 +15,21 @@ class SessionsController < ApplicationController
|
||||||
json: SessionHelper.json_hash(user).merge(config: config_frontend)
|
json: SessionHelper.json_hash(user).merge(config: config_frontend)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create_sso
|
||||||
|
authenticate_with_sso
|
||||||
|
|
||||||
|
redirect_to '/#'
|
||||||
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
user = authentication_check_only || authenticate_with_sso
|
user = authentication_check_only
|
||||||
|
raise Exceptions::NotAuthorized, 'no valid session' if user.blank?
|
||||||
|
|
||||||
initiate_session_for(user)
|
initiate_session_for(user)
|
||||||
|
|
||||||
# return current session
|
# return current session
|
||||||
render json: SessionHelper.json_hash(user).merge(config: config_frontend)
|
render json: SessionHelper.json_hash(user).merge(config: config_frontend)
|
||||||
rescue Exceptions::NotAuthorized => e
|
rescue Exceptions::NotAuthorized => e
|
||||||
raise if e.message != 'no valid session'
|
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
error: e.message,
|
error: e.message,
|
||||||
config: config_frontend,
|
config: config_frontend,
|
||||||
|
|
|
@ -5,6 +5,9 @@ Zammad::Application.routes.draw do
|
||||||
match '/auth/:provider/callback', to: 'sessions#create_omniauth', via: %i[post get puts delete]
|
match '/auth/:provider/callback', to: 'sessions#create_omniauth', via: %i[post get puts delete]
|
||||||
match '/auth/failure', to: 'sessions#failure_omniauth', via: %i[post get]
|
match '/auth/failure', to: 'sessions#failure_omniauth', via: %i[post get]
|
||||||
|
|
||||||
|
# sso
|
||||||
|
match '/auth/sso', to: 'sessions#create_sso', via: %i[get post]
|
||||||
|
|
||||||
# sessions
|
# sessions
|
||||||
match api_path + '/signin', to: 'sessions#create', via: :post
|
match api_path + '/signin', to: 'sessions#create', via: :post
|
||||||
match api_path + '/signshow', to: 'sessions#show', via: %i[get post]
|
match api_path + '/signshow', to: 'sessions#show', via: %i[get post]
|
||||||
|
|
|
@ -1,53 +1,38 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe 'Sessions endpoints', type: :request do
|
RSpec.describe 'Sessions endpoints', type: :request do
|
||||||
# The frontend sends a device fingerprint in the request parameters during authentication
|
|
||||||
# (as part of App.Auth.loginCheck() and App.WebSocket.auth()).
|
|
||||||
#
|
|
||||||
# Without this parameter, the controller will raise a 422 Unprocessable Entity error
|
|
||||||
# (in ApplicationController::HandlesDevices#user_device_log).
|
|
||||||
let(:fingerprint) { { fingerprint: 'foo' } }
|
|
||||||
|
|
||||||
describe 'GET /api/v1/signshow (single sign-on)' do
|
describe 'GET /auth/sso (single sign-on)' do
|
||||||
context 'with invalid user login' do
|
context 'with invalid user login' do
|
||||||
let(:login) { User.pluck(:login).max.next }
|
let(:login) { User.pluck(:login).max.next }
|
||||||
|
|
||||||
context 'in "REMOTE_USER" request env var' do
|
context 'in "REMOTE_USER" request env var' do
|
||||||
let(:env) { { 'REMOTE_USER' => login } }
|
let(:env) { { 'REMOTE_USER' => login } }
|
||||||
|
|
||||||
it 'returns invalid session response' do
|
it 'returns unauthorized response' do
|
||||||
get '/api/v1/signshow', as: :json, env: env, params: fingerprint
|
get '/auth/sso', as: :json, env: env
|
||||||
|
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:unauthorized)
|
||||||
expect(json_response)
|
|
||||||
.to include('error' => 'no valid session')
|
|
||||||
.and not_include('session')
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'in "HTTP_REMOTE_USER" request env var' do
|
context 'in "HTTP_REMOTE_USER" request env var' do
|
||||||
let(:env) { { 'HTTP_REMOTE_USER' => login } }
|
let(:env) { { 'HTTP_REMOTE_USER' => login } }
|
||||||
|
|
||||||
it 'returns invalid session response' do
|
it 'returns unauthorized response' do
|
||||||
get '/api/v1/signshow', as: :json, env: env, params: fingerprint
|
get '/auth/sso', as: :json, env: env
|
||||||
|
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:unauthorized)
|
||||||
expect(json_response)
|
|
||||||
.to include('error' => 'no valid session')
|
|
||||||
.and not_include('session')
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'in "X-Forwarded-User" request header' do
|
context 'in "X-Forwarded-User" request header' do
|
||||||
let(:headers) { { 'X-Forwarded-User' => login } }
|
let(:headers) { { 'X-Forwarded-User' => login } }
|
||||||
|
|
||||||
it 'returns invalid session response' do
|
it 'returns unauthorized response' do
|
||||||
get '/api/v1/signshow', as: :json, headers: headers, params: fingerprint
|
get '/auth/sso', as: :json, headers: headers
|
||||||
|
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:unauthorized)
|
||||||
expect(json_response)
|
|
||||||
.to include('error' => 'no valid session')
|
|
||||||
.and not_include('session')
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -63,7 +48,7 @@ RSpec.describe 'Sessions endpoints', type: :request do
|
||||||
let(:env) { { 'REMOTE_USER' => login } }
|
let(:env) { { 'REMOTE_USER' => login } }
|
||||||
|
|
||||||
it 'returns 401 unauthorized' do
|
it 'returns 401 unauthorized' do
|
||||||
get '/api/v1/signshow', as: :json, env: env, params: fingerprint
|
get '/auth/sso', as: :json, env: env
|
||||||
|
|
||||||
expect(response).to have_http_status(:unauthorized)
|
expect(response).to have_http_status(:unauthorized)
|
||||||
expect(json_response).to include('error' => 'Maintenance mode enabled!')
|
expect(json_response).to include('error' => 'Maintenance mode enabled!')
|
||||||
|
@ -74,7 +59,7 @@ RSpec.describe 'Sessions endpoints', type: :request do
|
||||||
let(:env) { { 'HTTP_REMOTE_USER' => login } }
|
let(:env) { { 'HTTP_REMOTE_USER' => login } }
|
||||||
|
|
||||||
it 'returns 401 unauthorized' do
|
it 'returns 401 unauthorized' do
|
||||||
get '/api/v1/signshow', as: :json, env: env, params: fingerprint
|
get '/auth/sso', as: :json, env: env
|
||||||
|
|
||||||
expect(response).to have_http_status(:unauthorized)
|
expect(response).to have_http_status(:unauthorized)
|
||||||
expect(json_response).to include('error' => 'Maintenance mode enabled!')
|
expect(json_response).to include('error' => 'Maintenance mode enabled!')
|
||||||
|
@ -85,7 +70,7 @@ RSpec.describe 'Sessions endpoints', type: :request do
|
||||||
let(:headers) { { 'X-Forwarded-User' => login } }
|
let(:headers) { { 'X-Forwarded-User' => login } }
|
||||||
|
|
||||||
it 'returns 401 unauthorized' do
|
it 'returns 401 unauthorized' do
|
||||||
get '/api/v1/signshow', as: :json, headers: headers, params: fingerprint
|
get '/auth/sso', as: :json, headers: headers
|
||||||
|
|
||||||
expect(response).to have_http_status(:unauthorized)
|
expect(response).to have_http_status(:unauthorized)
|
||||||
expect(json_response).to include('error' => 'Maintenance mode enabled!')
|
expect(json_response).to include('error' => 'Maintenance mode enabled!')
|
||||||
|
@ -97,81 +82,45 @@ RSpec.describe 'Sessions endpoints', type: :request do
|
||||||
let(:env) { { 'REMOTE_USER' => login } }
|
let(:env) { { 'REMOTE_USER' => login } }
|
||||||
|
|
||||||
it 'returns a new user-session response' do
|
it 'returns a new user-session response' do
|
||||||
get '/api/v1/signshow', as: :json, env: env, params: fingerprint
|
get '/auth/sso', as: :json, env: env
|
||||||
|
|
||||||
expect(json_response)
|
expect(response).to redirect_to('/#')
|
||||||
.to include('session' => hash_including('login' => login))
|
|
||||||
.and not_include('error')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sets the :user_id session parameter' do
|
it 'sets the :user_id session parameter' do
|
||||||
expect { get '/api/v1/signshow', as: :json, env: env, params: fingerprint }
|
expect { get '/auth/sso', as: :json, env: env }
|
||||||
.to change { request&.session&.fetch(:user_id) }.to(user.id)
|
.to change { request&.session&.fetch(:user_id) }.to(user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sets the :persistent session parameter' do
|
|
||||||
expect { get '/api/v1/signshow', as: :json, env: env, params: fingerprint }
|
|
||||||
.to change { request&.session&.fetch(:persistent) }.to(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'adds an activity stream entry for the user’s session' do
|
|
||||||
expect { get '/api/v1/signshow', as: :json, env: env, params: fingerprint }
|
|
||||||
.to change(ActivityStream, :count).by(1)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'in "HTTP_REMOTE_USER" request env var' do
|
context 'in "HTTP_REMOTE_USER" request env var' do
|
||||||
let(:env) { { 'HTTP_REMOTE_USER' => login } }
|
let(:env) { { 'HTTP_REMOTE_USER' => login } }
|
||||||
|
|
||||||
it 'returns a new user-session response' do
|
it 'returns a new user-session response' do
|
||||||
get '/api/v1/signshow', as: :json, env: env, params: fingerprint
|
get '/auth/sso', as: :json, env: env
|
||||||
|
|
||||||
expect(json_response)
|
expect(response).to redirect_to('/#')
|
||||||
.to include('session' => hash_including('login' => login))
|
|
||||||
.and not_include('error')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sets the :user_id session parameter' do
|
it 'sets the :user_id session parameter' do
|
||||||
expect { get '/api/v1/signshow', as: :json, env: env, params: fingerprint }
|
expect { get '/auth/sso', as: :json, env: env }
|
||||||
.to change { request&.session&.fetch(:user_id) }.to(user.id)
|
.to change { request&.session&.fetch(:user_id) }.to(user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sets the :persistent session parameter' do
|
|
||||||
expect { get '/api/v1/signshow', as: :json, env: env, params: fingerprint }
|
|
||||||
.to change { request&.session&.fetch(:persistent) }.to(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'adds an activity stream entry for the user’s session' do
|
|
||||||
expect { get '/api/v1/signshow', as: :json, env: env, params: fingerprint }
|
|
||||||
.to change(ActivityStream, :count).by(1)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'in "X-Forwarded-User" request header' do
|
context 'in "X-Forwarded-User" request header' do
|
||||||
let(:headers) { { 'X-Forwarded-User' => login } }
|
let(:headers) { { 'X-Forwarded-User' => login } }
|
||||||
|
|
||||||
it 'returns a new user-session response' do
|
it 'returns a new user-session response' do
|
||||||
get '/api/v1/signshow', as: :json, headers: headers, params: fingerprint
|
get '/auth/sso', as: :json, headers: headers
|
||||||
|
|
||||||
expect(json_response)
|
expect(response).to redirect_to('/#')
|
||||||
.to include('session' => hash_including('login' => login))
|
|
||||||
.and not_include('error')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sets the :user_id session parameter on the client' do
|
it 'sets the :user_id session parameter on the client' do
|
||||||
expect { get '/api/v1/signshow', as: :json, headers: headers, params: fingerprint }
|
expect { get '/auth/sso', as: :json, headers: headers }
|
||||||
.to change { request&.session&.fetch(:user_id) }.to(user.id)
|
.to change { request&.session&.fetch(:user_id) }.to(user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sets the :persistent session parameter' do
|
|
||||||
expect { get '/api/v1/signshow', as: :json, headers: headers, params: fingerprint }
|
|
||||||
.to change { request&.session&.fetch(:persistent) }.to(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'adds an activity stream entry for the user’s session' do
|
|
||||||
expect { get '/api/v1/signshow', as: :json, headers: headers, params: fingerprint }
|
|
||||||
.to change(ActivityStream, :count).by(1)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue