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",
|
||||
"url": "",
|
||||
"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'
|
||||
config: 'auth_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-spinner-small { width: 15px; height: 15px; }
|
||||
.icon-split { width: 16px; height: 17px; }
|
||||
.icon-sso-button { width: 29px; height: 24px; }
|
||||
.icon-status-modified-outer-circle { width: 16px; height: 16px; }
|
||||
.icon-status { width: 16px; height: 16px; }
|
||||
.icon-stopwatch { width: 77px; height: 83px; }
|
||||
|
|
|
@ -3199,6 +3199,10 @@ ol.tabs li {
|
|||
background: hsl(0,0%,27%);
|
||||
}
|
||||
|
||||
&.auth-provider--sso {
|
||||
background: #454545;
|
||||
}
|
||||
|
||||
.provider-name {
|
||||
flex: 1;
|
||||
}
|
||||
|
@ -3399,6 +3403,10 @@ ol.tabs li {
|
|||
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
|
||||
|
|
|
@ -141,21 +141,6 @@ module ApplicationController::Authenticates
|
|||
authentication_check_prerequesits(user, 'session', {})
|
||||
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)
|
||||
raise Exceptions::NotAuthorized, 'Maintenance mode enabled!' if in_maintenance_mode?(user)
|
||||
|
||||
|
|
|
@ -16,7 +16,21 @@ class SessionsController < ApplicationController
|
|||
end
|
||||
|
||||
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 '/#'
|
||||
end
|
||||
|
|
|
@ -31,6 +31,9 @@
|
|||
ProxyPass /ws ws://127.0.0.1:6042/
|
||||
ProxyPass / http://127.0.0.1:3000/
|
||||
|
||||
# change this line in an SSO setup
|
||||
RequestHeader unset X-Forwarded-User
|
||||
|
||||
DocumentRoot "/opt/zammad/public"
|
||||
|
||||
<Directory />
|
||||
|
|
|
@ -54,6 +54,9 @@
|
|||
ProxyPass /ws ws://127.0.0.1:6042/
|
||||
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
|
||||
# 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
|
||||
|
|
|
@ -46,6 +46,10 @@ server {
|
|||
proxy_set_header CLIENT_IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
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_pass http://zammad-railsserver;
|
||||
|
||||
|
|
|
@ -100,6 +100,10 @@ server {
|
|||
proxy_set_header Host $http_host;
|
||||
proxy_set_header CLIENT_IP $remote_addr;
|
||||
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_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,
|
||||
)
|
||||
|
||||
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
|
||||
</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"/>
|
||||
</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">
|
||||
<title>
|
||||
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
|
||||
|
||||
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
|
||||
let(:login) { User.pluck(:login).max.next }
|
||||
|
||||
|
|
Loading…
Reference in a new issue