Fixes #3128 - Add SSO login button to login page.
This commit is contained in:
parent
f6c2d810bc
commit
8d140037e4
15 changed files with 172 additions and 16 deletions
|
@ -688,5 +688,10 @@
|
||||||
"author": "Felix Niklas",
|
"author": "Felix Niklas",
|
||||||
"url": "",
|
"url": "",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"sso-button.svg": {
|
||||||
|
"author": "Tanu Doank",
|
||||||
|
"url": "https://thenounproject.com/term/key/1247931/",
|
||||||
|
"license": "CC 3.0 Attribution"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -111,4 +111,9 @@ App.Config.set('auth_provider_all', {
|
||||||
name: 'SAML'
|
name: 'SAML'
|
||||||
config: 'auth_saml'
|
config: 'auth_saml'
|
||||||
class: 'saml'
|
class: 'saml'
|
||||||
|
sso:
|
||||||
|
url: '/auth/sso'
|
||||||
|
name: 'SSO'
|
||||||
|
config: 'auth_sso'
|
||||||
|
class: 'sso'
|
||||||
})
|
})
|
||||||
|
|
|
@ -114,6 +114,7 @@
|
||||||
.icon-sms { width: 17px; height: 17px; }
|
.icon-sms { width: 17px; height: 17px; }
|
||||||
.icon-spinner-small { width: 15px; height: 15px; }
|
.icon-spinner-small { width: 15px; height: 15px; }
|
||||||
.icon-split { width: 16px; height: 17px; }
|
.icon-split { width: 16px; height: 17px; }
|
||||||
|
.icon-sso-button { width: 29px; height: 24px; }
|
||||||
.icon-status-modified-outer-circle { width: 16px; height: 16px; }
|
.icon-status-modified-outer-circle { width: 16px; height: 16px; }
|
||||||
.icon-status { width: 16px; height: 16px; }
|
.icon-status { width: 16px; height: 16px; }
|
||||||
.icon-stopwatch { width: 77px; height: 83px; }
|
.icon-stopwatch { width: 77px; height: 83px; }
|
||||||
|
|
|
@ -3199,6 +3199,10 @@ ol.tabs li {
|
||||||
background: hsl(0,0%,27%);
|
background: hsl(0,0%,27%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.auth-provider--sso {
|
||||||
|
background: #454545;
|
||||||
|
}
|
||||||
|
|
||||||
.provider-name {
|
.provider-name {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
@ -3399,6 +3403,10 @@ ol.tabs li {
|
||||||
fill: white;
|
fill: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-sso-button {
|
||||||
|
fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* removed margin of forms to not break the layout with submit buttons within <form></form> area e. g. for modal dialogs
|
* removed margin of forms to not break the layout with submit buttons within <form></form> area e. g. for modal dialogs
|
||||||
|
|
|
@ -141,21 +141,6 @@ module ApplicationController::Authenticates
|
||||||
authentication_check_prerequesits(user, 'session', {})
|
authentication_check_prerequesits(user, 'session', {})
|
||||||
end
|
end
|
||||||
|
|
||||||
def authenticate_with_sso
|
|
||||||
user = begin
|
|
||||||
login = request.env['REMOTE_USER'] ||
|
|
||||||
request.env['HTTP_REMOTE_USER'] ||
|
|
||||||
request.headers['X-Forwarded-User']
|
|
||||||
User.lookup(login: login&.downcase)
|
|
||||||
end
|
|
||||||
|
|
||||||
raise Exceptions::NotAuthorized, 'Missing SSO ENV REMOTE_USER' if login.blank?
|
|
||||||
raise Exceptions::NotAuthorized, "No such user #{login} from ENV REMOTE_USER" if !user
|
|
||||||
|
|
||||||
session.delete(:switched_from_user_id)
|
|
||||||
authentication_check_prerequesits(user, 'SSO', {})
|
|
||||||
end
|
|
||||||
|
|
||||||
def authentication_check_prerequesits(user, auth_type, auth_param)
|
def authentication_check_prerequesits(user, auth_type, auth_param)
|
||||||
raise Exceptions::NotAuthorized, 'Maintenance mode enabled!' if in_maintenance_mode?(user)
|
raise Exceptions::NotAuthorized, 'Maintenance mode enabled!' if in_maintenance_mode?(user)
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,21 @@ class SessionsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_sso
|
def create_sso
|
||||||
authenticate_with_sso
|
raise Exceptions::NotAuthorized, 'SSO authentication disabled!' if !Setting.get('auth_sso')
|
||||||
|
|
||||||
|
user = begin
|
||||||
|
login = request.env['REMOTE_USER'] ||
|
||||||
|
request.env['HTTP_REMOTE_USER'] ||
|
||||||
|
request.headers['X-Forwarded-User']
|
||||||
|
|
||||||
|
User.lookup(login: login&.downcase)
|
||||||
|
end
|
||||||
|
|
||||||
|
raise Exceptions::NotAuthorized, 'Missing SSO ENV REMOTE_USER or X-Forwarded-User header' if login.blank?
|
||||||
|
raise Exceptions::NotAuthorized, "No such user '#{login}' found!" if user.blank?
|
||||||
|
|
||||||
|
session.delete(:switched_from_user_id)
|
||||||
|
authentication_check_prerequesits(user, 'SSO', {})
|
||||||
|
|
||||||
redirect_to '/#'
|
redirect_to '/#'
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,6 +31,9 @@
|
||||||
ProxyPass /ws ws://127.0.0.1:6042/
|
ProxyPass /ws ws://127.0.0.1:6042/
|
||||||
ProxyPass / http://127.0.0.1:3000/
|
ProxyPass / http://127.0.0.1:3000/
|
||||||
|
|
||||||
|
# change this line in an SSO setup
|
||||||
|
RequestHeader unset X-Forwarded-User
|
||||||
|
|
||||||
DocumentRoot "/opt/zammad/public"
|
DocumentRoot "/opt/zammad/public"
|
||||||
|
|
||||||
<Directory />
|
<Directory />
|
||||||
|
|
|
@ -54,6 +54,9 @@
|
||||||
ProxyPass /ws ws://127.0.0.1:6042/
|
ProxyPass /ws ws://127.0.0.1:6042/
|
||||||
ProxyPass / http://127.0.0.1:3000/
|
ProxyPass / http://127.0.0.1:3000/
|
||||||
|
|
||||||
|
# change this line in an SSO setup
|
||||||
|
RequestHeader unset X-Forwarded-User
|
||||||
|
|
||||||
# Use settings below if proxying does not work and you receive HTTP-Errror 404
|
# Use settings below if proxying does not work and you receive HTTP-Errror 404
|
||||||
# if you use the settings below, make sure to comment out the above two options
|
# if you use the settings below, make sure to comment out the above two options
|
||||||
# This may not apply to all systems, applies to openSuse
|
# This may not apply to all systems, applies to openSuse
|
||||||
|
|
|
@ -46,6 +46,10 @@ server {
|
||||||
proxy_set_header CLIENT_IP $remote_addr;
|
proxy_set_header CLIENT_IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
# Change this line in an SSO setup
|
||||||
|
proxy_set_header X-Forwarded-User "";
|
||||||
|
|
||||||
proxy_read_timeout 300;
|
proxy_read_timeout 300;
|
||||||
proxy_pass http://zammad-railsserver;
|
proxy_pass http://zammad-railsserver;
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,10 @@ server {
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $http_host;
|
||||||
proxy_set_header CLIENT_IP $remote_addr;
|
proxy_set_header CLIENT_IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
|
||||||
|
# change this line in an SSO setup
|
||||||
|
proxy_set_header X-Forwarded-User "";
|
||||||
|
|
||||||
proxy_read_timeout 180;
|
proxy_read_timeout 180;
|
||||||
proxy_pass http://zammad-railsserver;
|
proxy_pass http://zammad-railsserver;
|
||||||
|
|
||||||
|
|
38
db/migrate/20200724130426_issue3128_add_sso.rb
Normal file
38
db/migrate/20200724130426_issue3128_add_sso.rb
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
class Issue3128AddSso < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
|
||||||
|
# return if it's a new setup
|
||||||
|
return if !Setting.exists?(name: 'system_init_done')
|
||||||
|
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'Authentication via %s',
|
||||||
|
name: 'auth_sso',
|
||||||
|
area: 'Security::ThirdPartyAuthentication',
|
||||||
|
description: 'Enables button for user authentication via %s. The button will redirect to /auth/sso on user interaction.',
|
||||||
|
options: {
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
display: '',
|
||||||
|
null: true,
|
||||||
|
name: 'auth_sso',
|
||||||
|
tag: 'boolean',
|
||||||
|
options: {
|
||||||
|
true => 'yes',
|
||||||
|
false => 'no',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
preferences: {
|
||||||
|
controller: 'SettingsAreaSwitch',
|
||||||
|
sub: {},
|
||||||
|
title_i18n: ['SSO'],
|
||||||
|
description_i18n: ['SSO', 'Button for Single Sign On.'],
|
||||||
|
permission: ['admin.security'],
|
||||||
|
},
|
||||||
|
state: false,
|
||||||
|
frontend: true
|
||||||
|
)
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -4613,3 +4613,33 @@ Setting.create_if_not_exists(
|
||||||
},
|
},
|
||||||
frontend: true,
|
frontend: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'Authentication via %s',
|
||||||
|
name: 'auth_sso',
|
||||||
|
area: 'Security::ThirdPartyAuthentication',
|
||||||
|
description: 'Enables button for user authentication via %s. The button will redirect to /auth/sso on user interaction.',
|
||||||
|
options: {
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
display: '',
|
||||||
|
null: true,
|
||||||
|
name: 'auth_sso',
|
||||||
|
tag: 'boolean',
|
||||||
|
options: {
|
||||||
|
true => 'yes',
|
||||||
|
false => 'no',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
preferences: {
|
||||||
|
controller: 'SettingsAreaSwitch',
|
||||||
|
sub: {},
|
||||||
|
title_i18n: ['SSO'],
|
||||||
|
description_i18n: ['SSO', 'Button for Single Sign On.'],
|
||||||
|
permission: ['admin.security'],
|
||||||
|
},
|
||||||
|
state: false,
|
||||||
|
frontend: true
|
||||||
|
)
|
||||||
|
|
|
@ -763,6 +763,9 @@
|
||||||
split
|
split
|
||||||
</title>
|
</title>
|
||||||
<path d="M4.443 6.218l.42-.343C6.415 4.589 7 3.514 7 1h2c0 2.514.584 3.589 2.138 4.875l.42.343C13.293 7.64 14 8.773 14 11h-4l3 3.333L16 11h-4c0-1.526-.393-2.156-1.71-3.235.038.031-.327-.266-.428-.35C8.945 6.655 8.5 6.5 8 5.4c-.5 1.1-.945 1.256-1.862 2.015-.101.084-.466.381-.428.35C4.393 8.845 4 9.474 4 11H2h4l-3 3.333L0 11h2c0-2.227.706-3.36 2.443-4.782z" fill-rule="evenodd"/>
|
<path d="M4.443 6.218l.42-.343C6.415 4.589 7 3.514 7 1h2c0 2.514.584 3.589 2.138 4.875l.42.343C13.293 7.64 14 8.773 14 11h-4l3 3.333L16 11h-4c0-1.526-.393-2.156-1.71-3.235.038.031-.327-.266-.428-.35C8.945 6.655 8.5 6.5 8 5.4c-.5 1.1-.945 1.256-1.862 2.015-.101.084-.466.381-.428.35C4.393 8.845 4 9.474 4 11H2h4l-3 3.333L0 11h2c0-2.227.706-3.36 2.443-4.782z" fill-rule="evenodd"/>
|
||||||
|
</symbol><symbol id="icon-sso-button" viewBox="0 0 96.57 79.92">
|
||||||
|
<path d="M10.41 41.942c0-1.657 1.325-2.983 3.314-2.983a2.97 2.97 0 0 1 2.982 2.983 2.97 2.97 0 0 1-2.982 2.982c-1.989 0-3.314-1.325-3.314-2.982zm21.872-4.64c0-1.325.994-2.32 2.32-2.32 1.325 0 2.32.995 2.32 2.32 0 1.326-.995 1.988-2.32 1.988-1.326 0-2.32-.662-2.32-1.988zm-7.622 0c0-.663.662-1.325 1.657-1.325.662 0 1.325.662 1.325 1.325 0 .663-.663 1.326-1.325 1.326-.995 0-1.657-.663-1.657-1.326zm18.226 1.988c-.994 0-1.988-.662-1.988-1.988 0-1.325.994-2.32 1.988-2.32 1.326 0 2.32.995 2.32 2.32 0 1.326-.994 1.988-2.32 1.988zm-8.285 5.634c-.662 0-1.325-.663-1.325-1.325 0-.995.663-1.657 1.325-1.657.663 0 1.326.662 1.326 1.657 0 .662-.663 1.325-1.326 1.325zm20.547-4.64c0-2.65.662-5.302 1.325-7.621.663-1.326.994-2.651 1.657-3.646h-8.616c-.663 0-.994.663-.994 1.326v2.32H13.06c-4.97 0-9.61 2.65-12.593 7.622 2.983 4.64 7.622 7.29 12.593 7.29H48.52v2.651c0 .663.331.995.994.995h8.616c-.663-.995-.994-2.32-1.657-3.646-.663-2.32-1.325-4.64-1.325-7.29z"/>
|
||||||
|
<path d="M86.63 40.285c0 2.32-2.32 4.64-4.64 4.64-2.65 0-4.97-2.32-4.97-4.64 0-2.652 2.32-4.64 4.97-4.64 2.32 0 4.64 1.988 4.64 4.64zm9.61 0c0-10.936-8.616-19.552-19.22-19.552-7.291 0-13.588 3.976-16.902 9.61-1.656 2.982-2.65 6.296-2.65 9.942 0 3.314.994 6.627 2.65 9.61 3.314 5.634 9.61 9.61 16.901 9.61 10.605 0 19.221-8.616 19.221-19.22z"/>
|
||||||
</symbol><symbol id="icon-status-modified-outer-circle" viewBox="0 0 16 16">
|
</symbol><symbol id="icon-status-modified-outer-circle" viewBox="0 0 16 16">
|
||||||
<title>
|
<title>
|
||||||
status-modified-outer-circle
|
status-modified-outer-circle
|
||||||
|
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 88 KiB |
32
public/assets/images/icons/sso-button.svg
Normal file
32
public/assets/images/icons/sso-button.svg
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
y="0px"
|
||||||
|
x="0px"
|
||||||
|
viewBox="0 0 96.57 79.92"
|
||||||
|
style="image-rendering:optimizeQuality;shape-rendering:geometricPrecision;text-rendering:geometricPrecision"
|
||||||
|
version="1.1"
|
||||||
|
xml:space="preserve"
|
||||||
|
fill="#50e3c2"
|
||||||
|
width="29"
|
||||||
|
height="24">
|
||||||
|
<defs
|
||||||
|
id="defs24" />
|
||||||
|
<g
|
||||||
|
transform="matrix(0.33139046,0,0,0.33139046,-6.8223822,-15.05759)"
|
||||||
|
id="g30"
|
||||||
|
fill="#50e3c2">
|
||||||
|
<path
|
||||||
|
id="path26"
|
||||||
|
d="m 52,172 c 0,-5 4,-9 10,-9 5,0 9,4 9,9 0,5 -4,9 -9,9 -6,0 -10,-4 -10,-9 z m 66,-14 c 0,-4 3,-7 7,-7 4,0 7,3 7,7 0,4 -3,6 -7,6 -4,0 -7,-2 -7,-6 z m -23,0 c 0,-2 2,-4 5,-4 2,0 4,2 4,4 0,2 -2,4 -4,4 -3,0 -5,-2 -5,-4 z m 55,6 c -3,0 -6,-2 -6,-6 0,-4 3,-7 6,-7 4,0 7,3 7,7 0,4 -3,6 -7,6 z m -25,17 c -2,0 -4,-2 -4,-4 0,-3 2,-5 4,-5 2,0 4,2 4,5 0,2 -2,4 -4,4 z m 62,-14 c 0,-8 2,-16 4,-23 2,-4 3,-8 5,-11 h -26 c -2,0 -3,2 -3,4 v 7 H 60 c -15,0 -29,8 -38,23 9,14 23,22 38,22 h 107 v 8 c 0,2 1,3 3,3 h 26 c -2,-3 -3,-7 -5,-11 -2,-7 -4,-14 -4,-22 z" />
|
||||||
|
<path
|
||||||
|
id="path28"
|
||||||
|
d="m 282,167 c 0,7 -7,14 -14,14 -8,0 -15,-7 -15,-14 0,-8 7,-14 15,-14 7,0 14,6 14,14 z m 29,0 c 0,-33 -26,-59 -58,-59 -22,0 -41,12 -51,29 -5,9 -8,19 -8,30 0,10 3,20 8,29 10,17 29,29 51,29 32,0 58,-26 58,-58 z" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -30,6 +30,27 @@ RSpec.describe 'Sessions endpoints', type: :request do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET /auth/sso (single sign-on)' do
|
describe 'GET /auth/sso (single sign-on)' do
|
||||||
|
|
||||||
|
before do
|
||||||
|
Setting.set('auth_sso', true)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when SSO is disabled' do
|
||||||
|
|
||||||
|
before do
|
||||||
|
Setting.set('auth_sso', false)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:headers) { { 'X-Forwarded-User' => login } }
|
||||||
|
let(:login) { User.last.login }
|
||||||
|
|
||||||
|
it 'returns a new user-session response' do
|
||||||
|
get '/auth/sso', as: :json, headers: headers
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
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 }
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue