Merge "Support for Keystone as Identity Provider"
This commit is contained in:
commit
5f15bbb1b6
144
manifests/federation/identity_provider.pp
Normal file
144
manifests/federation/identity_provider.pp
Normal file
@ -0,0 +1,144 @@
|
||||
# == class: keystone::federation::identity_provider
|
||||
#
|
||||
# == Parameters
|
||||
#
|
||||
# [*certfile*]
|
||||
# (Required) Path of the certfile for SAML signing. The path can not
|
||||
# contain a comma. (string value).
|
||||
# Defaults to $::keystone::ssl_ca_certs value.
|
||||
#
|
||||
# [*keyfile*]
|
||||
# (Required) Path of the keyfile for SAML signing. The path can not
|
||||
# contain a comma (string value).
|
||||
# Defaults to $::keystone::ssl_ca_key value.
|
||||
#
|
||||
# [*idp_entity_id*]
|
||||
# (Required) Entity ID value for unique Identity Provider identification
|
||||
# (string value).
|
||||
#
|
||||
# [*idp_sso_endpoint*]
|
||||
# (Required) Identity Provider Single-Sign-On service value (string value).
|
||||
#
|
||||
# [*idp_metadata_path*]
|
||||
# (Required) Path to the Identity Provider Metadata file (string value).
|
||||
#
|
||||
# [*idp_organization_name*]
|
||||
# (Optional) Organization name the installation belongs to (string value).
|
||||
# Defaults to 'undef'.
|
||||
#
|
||||
# [*idp_organization_display_name*]
|
||||
# (Optional) Organization name to be displayed (string value).
|
||||
# Defaults to 'undef'.
|
||||
#
|
||||
# [*idp_organization_url*]
|
||||
# (Optional) URL of the organization (string value).
|
||||
# Defaults to 'undef'.
|
||||
#
|
||||
# [*idp_contact_company*]
|
||||
# (Optional) Company of contact person (string value).
|
||||
# Defaults to 'undef'.
|
||||
#
|
||||
# [*idp_contact_name*]
|
||||
# (Optional) Given name of contact person (string value).
|
||||
# Defaults to 'undef'.
|
||||
#
|
||||
# [*idp_contact_surname*]
|
||||
# (Optional) Surname of contact person (string value).
|
||||
# Defaults to 'undef'.
|
||||
#
|
||||
# [*idp_contact_email*]
|
||||
# (Optional) Email address of contact person (string value).
|
||||
# Defaults to 'undef'.
|
||||
#
|
||||
# [*idp_contact_telephone*]
|
||||
# (Optional) Telephone number of contact person (string value).
|
||||
# Defaults to 'undef'.
|
||||
#
|
||||
# [*idp_contact_type*]
|
||||
# (Optional) Contact type. Allowed values are: technical, support,
|
||||
# administrative billing, and other (string value).
|
||||
# Defaults to 'undef'.
|
||||
#
|
||||
# [*user*]
|
||||
# (Optional) User with access to keystone files. (string value)
|
||||
# Defaults to 'keystone'.
|
||||
#
|
||||
# == Dependencies
|
||||
# == Examples
|
||||
# == Authors
|
||||
#
|
||||
# Iury Gregory iurygregory@gmail.com
|
||||
#
|
||||
# == Copyright
|
||||
#
|
||||
# Copyright 2013 eNovance <licensing@enovance.com>
|
||||
#
|
||||
class keystone::federation::identity_provider(
|
||||
$idp_entity_id,
|
||||
$idp_sso_endpoint,
|
||||
$idp_metadata_path,
|
||||
$certfile = $::keystone::ssl_ca_certs,
|
||||
$keyfile = $::keystone::ssl_ca_key,
|
||||
$user = 'keystone',
|
||||
$idp_organization_name = undef,
|
||||
$idp_organization_display_name = undef,
|
||||
$idp_organization_url = undef,
|
||||
$idp_contact_company = undef,
|
||||
$idp_contact_name = undef,
|
||||
$idp_contact_surname = undef,
|
||||
$idp_contact_email = undef,
|
||||
$idp_contact_telephone = undef,
|
||||
$idp_contact_type = undef,
|
||||
) {
|
||||
include ::keystone::params
|
||||
|
||||
if $::keystone::service_name != 'httpd' {
|
||||
fail ('Keystone need to be running under Apache for Federation work.')
|
||||
}
|
||||
|
||||
ensure_packages(['xmlsec1','python-pysaml2'], {
|
||||
ensure => present
|
||||
})
|
||||
|
||||
keystone_config {
|
||||
'saml/certfile': value => $certfile;
|
||||
'saml/keyfile': value => $keyfile;
|
||||
'saml/idp_entity_id': value => $idp_entity_id;
|
||||
'saml/idp_sso_endpoint': value => $idp_sso_endpoint;
|
||||
'saml/idp_metadata_path': value => $idp_metadata_path;
|
||||
'saml/idp_organization_name': value => $idp_organization_name;
|
||||
'saml/idp_organization_display_name': value => $idp_organization_display_name;
|
||||
'saml/idp_organization_url': value => $idp_organization_url;
|
||||
'saml/idp_contact_company': value => $idp_contact_company;
|
||||
'saml/idp_contact_name': value => $idp_contact_name;
|
||||
'saml/idp_contact_surname': value => $idp_contact_surname;
|
||||
'saml/idp_contact_email': value => $idp_contact_email;
|
||||
'saml/idp_contact_telephone': value => $idp_contact_telephone;
|
||||
}
|
||||
|
||||
if $idp_contact_type and !($idp_contact_type in ['technical','support','administrative','billing','other']) {
|
||||
fail('Allowed values for idp_contact_type are: technical, support, administrative, billing and other')
|
||||
} else{
|
||||
keystone_config {
|
||||
'saml/idp_contact_type': value => $idp_contact_type;
|
||||
}
|
||||
}
|
||||
|
||||
exec {'saml_idp_metadata':
|
||||
path => '/usr/bin',
|
||||
user => "${user}",
|
||||
command => "keystone-manage saml_idp_metadata > ${idp_metadata_path}",
|
||||
creates => $idp_metadata_path,
|
||||
notify => Service[$::keystone::params::service_name],
|
||||
subscribe => Package['keystone'],
|
||||
}
|
||||
|
||||
file { $idp_metadata_path:
|
||||
ensure => present,
|
||||
mode => '0600',
|
||||
owner => "${user}",
|
||||
}
|
||||
|
||||
Keystone_config<||> -> Exec<| title == 'saml_idp_metadata'|>
|
||||
|
||||
}
|
251
spec/acceptance/keystone_federation_identity_provider_spec.rb
Normal file
251
spec/acceptance/keystone_federation_identity_provider_spec.rb
Normal file
@ -0,0 +1,251 @@
|
||||
require 'spec_helper_acceptance'
|
||||
|
||||
describe 'keystone server running with Apache/WSGI as Identity Provider' do
|
||||
|
||||
context 'default parameters' do
|
||||
|
||||
it 'should work with no errors' do
|
||||
pp= <<-EOS
|
||||
Exec { logoutput => 'on_failure' }
|
||||
|
||||
# Common resources
|
||||
case $::osfamily {
|
||||
'Debian': {
|
||||
include ::apt
|
||||
apt::ppa { 'ppa:ubuntu-cloud-archive/liberty-staging':
|
||||
# it's false by default in 2.x series but true in 1.8.x
|
||||
package_manage => false,
|
||||
}
|
||||
Exec['apt_update'] -> Package<||>
|
||||
}
|
||||
'RedHat': {
|
||||
class { '::openstack_extras::repo::redhat::redhat':
|
||||
manage_rdo => false,
|
||||
repo_hash => {
|
||||
# we need kilo repo to be installed for dependencies
|
||||
'rdo-kilo' => {
|
||||
'baseurl' => 'https://repos.fedorapeople.org/repos/openstack/openstack-kilo/el7/',
|
||||
'descr' => 'RDO kilo',
|
||||
'gpgcheck' => 'no',
|
||||
},
|
||||
'rdo-liberty' => {
|
||||
'baseurl' => 'http://trunk.rdoproject.org/centos7/current/',
|
||||
'descr' => 'RDO trunk',
|
||||
'gpgcheck' => 'no',
|
||||
},
|
||||
},
|
||||
}
|
||||
package { 'openstack-selinux': ensure => 'latest' }
|
||||
}
|
||||
default: {
|
||||
fail("Unsupported osfamily (${::osfamily})")
|
||||
}
|
||||
}
|
||||
|
||||
class { '::mysql::server': }
|
||||
|
||||
# Keystone resources
|
||||
class { '::keystone::client': }
|
||||
class { '::keystone::cron::token_flush': }
|
||||
class { '::keystone::db::mysql':
|
||||
password => 'keystone',
|
||||
}
|
||||
class { '::keystone':
|
||||
verbose => true,
|
||||
debug => true,
|
||||
database_connection => 'mysql://keystone:keystone@127.0.0.1/keystone',
|
||||
admin_token => 'admin_token',
|
||||
enabled => true,
|
||||
service_name => 'httpd',
|
||||
default_domain => 'default_domain',
|
||||
}
|
||||
include ::apache
|
||||
class { '::keystone::wsgi::apache':
|
||||
ssl => false,
|
||||
}
|
||||
|
||||
# "v2" admin and service
|
||||
class { '::keystone::roles::admin':
|
||||
email => 'test@example.tld',
|
||||
password => 'a_big_secret',
|
||||
}
|
||||
class { '::keystone::endpoint':
|
||||
public_url => "http://127.0.0.1:5000/",
|
||||
admin_url => "http://127.0.0.1:35357/",
|
||||
default_domain => 'admin',
|
||||
}
|
||||
::keystone::resource::service_identity { 'beaker-ci':
|
||||
service_type => 'beaker',
|
||||
service_description => 'beaker service',
|
||||
service_name => 'beaker',
|
||||
password => 'secret',
|
||||
public_url => 'http://127.0.0.1:1234',
|
||||
admin_url => 'http://127.0.0.1:1234',
|
||||
internal_url => 'http://127.0.0.1:1234',
|
||||
}
|
||||
# v3 admin
|
||||
# we don't use ::keystone::roles::admin but still create resources manually:
|
||||
keystone_domain { 'admin_domain':
|
||||
ensure => present,
|
||||
enabled => true,
|
||||
description => 'Domain for admin v3 users',
|
||||
}
|
||||
keystone_domain { 'service_domain':
|
||||
ensure => present,
|
||||
enabled => true,
|
||||
description => 'Domain for admin v3 users',
|
||||
}
|
||||
keystone_tenant { 'servicesv3':
|
||||
ensure => present,
|
||||
enabled => true,
|
||||
description => 'Tenant for the openstack services',
|
||||
domain => 'service_domain',
|
||||
}
|
||||
keystone_tenant { 'openstackv3':
|
||||
ensure => present,
|
||||
enabled => true,
|
||||
description => 'admin tenant',
|
||||
domain => 'admin_domain',
|
||||
}
|
||||
keystone_user { 'adminv3':
|
||||
ensure => present,
|
||||
enabled => true,
|
||||
tenant => 'openstackv3', # note: don't have to use 'openstackv3::admin_domain' here since the tenant name 'openstackv3' is unique among all domains
|
||||
email => 'test@example.tld',
|
||||
password => 'a_big_secret',
|
||||
domain => 'admin_domain',
|
||||
}
|
||||
keystone_user_role { 'adminv3@openstackv3':
|
||||
ensure => present,
|
||||
roles => ['admin'],
|
||||
}
|
||||
# service user exists only in the service_domain - must
|
||||
# use v3 api
|
||||
::keystone::resource::service_identity { 'beaker-civ3':
|
||||
service_type => 'beakerv3',
|
||||
service_description => 'beakerv3 service',
|
||||
service_name => 'beakerv3',
|
||||
password => 'secret',
|
||||
tenant => 'servicesv3',
|
||||
public_url => 'http://127.0.0.1:1234/v3',
|
||||
admin_url => 'http://127.0.0.1:1234/v3',
|
||||
internal_url => 'http://127.0.0.1:1234/v3',
|
||||
user_domain => 'service_domain',
|
||||
project_domain => 'service_domain',
|
||||
}
|
||||
class { '::keystone::federation::identity_provider':
|
||||
idp_entity_id => 'http://127.0.0.1:5000/v3/OS-FEDERATION/saml2/idp',
|
||||
idp_sso_endpoint => 'http://127.0.0.1:5000/v3/OS-FEDERATION/saml2/sso',
|
||||
idp_metadata_path => '/etc/keystone/saml2_idp_metadata.xml',
|
||||
}
|
||||
EOS
|
||||
|
||||
|
||||
# Run it twice and test for idempotency
|
||||
apply_manifest(pp, :catch_failures => true)
|
||||
apply_manifest(pp, :catch_changes => true)
|
||||
end
|
||||
|
||||
describe port(5000) do
|
||||
it { is_expected.to be_listening }
|
||||
end
|
||||
|
||||
describe port(35357) do
|
||||
it { is_expected.to be_listening }
|
||||
end
|
||||
|
||||
describe cron do
|
||||
it { is_expected.to have_entry('1 0 * * * keystone-manage token_flush >>/var/log/keystone/keystone-tokenflush.log 2>&1').with_user('keystone') }
|
||||
end
|
||||
|
||||
shared_examples_for 'keystone user/tenant/service/role/endpoint resources using v2 API' do |auth_creds|
|
||||
it 'should find users in the default domain' do
|
||||
shell("openstack #{auth_creds} --os-auth-url http://127.0.0.1:5000/v2.0 --os-identity-api-version 2 user list") do |r|
|
||||
expect(r.stdout).to match(/admin/)
|
||||
expect(r.stderr).to be_empty
|
||||
end
|
||||
end
|
||||
it 'should find tenants in the default domain' do
|
||||
shell("openstack #{auth_creds} --os-auth-url http://127.0.0.1:5000/v2.0 --os-identity-api-version 2 project list") do |r|
|
||||
expect(r.stdout).to match(/openstack/)
|
||||
expect(r.stderr).to be_empty
|
||||
end
|
||||
end
|
||||
it 'should find beaker service' do
|
||||
shell("openstack #{auth_creds} --os-auth-url http://127.0.0.1:5000/v2.0 --os-identity-api-version 2 service list") do |r|
|
||||
expect(r.stdout).to match(/beaker/)
|
||||
expect(r.stderr).to be_empty
|
||||
end
|
||||
end
|
||||
it 'should find admin role' do
|
||||
shell("openstack #{auth_creds} --os-auth-url http://127.0.0.1:5000/v2.0 --os-identity-api-version 2 role list") do |r|
|
||||
expect(r.stdout).to match(/admin/)
|
||||
expect(r.stderr).to be_empty
|
||||
end
|
||||
end
|
||||
it 'should find beaker endpoints' do
|
||||
shell("openstack #{auth_creds} --os-auth-url http://127.0.0.1:5000/v2.0 --os-identity-api-version 2 endpoint list --long") do |r|
|
||||
expect(r.stdout).to match(/1234/)
|
||||
expect(r.stderr).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
shared_examples_for 'keystone user/tenant/service/role/endpoint resources using v3 API' do |auth_creds|
|
||||
it 'should find beaker user' do
|
||||
shell("openstack #{auth_creds} --os-auth-url http://127.0.0.1:5000/v3 --os-identity-api-version 3 user list") do |r|
|
||||
expect(r.stdout).to match(/beaker/)
|
||||
expect(r.stderr).to be_empty
|
||||
end
|
||||
end
|
||||
it 'should find services tenant' do
|
||||
shell("openstack #{auth_creds} --os-auth-url http://127.0.0.1:5000/v3 --os-identity-api-version 3 project list") do |r|
|
||||
expect(r.stdout).to match(/services/)
|
||||
expect(r.stderr).to be_empty
|
||||
end
|
||||
end
|
||||
it 'should find beaker service' do
|
||||
shell("openstack #{auth_creds} --os-auth-url http://127.0.0.1:5000/v3 --os-identity-api-version 3 service list") do |r|
|
||||
expect(r.stdout).to match(/beaker/)
|
||||
expect(r.stderr).to be_empty
|
||||
end
|
||||
end
|
||||
it 'should find admin role' do
|
||||
shell("openstack #{auth_creds} --os-auth-url http://127.0.0.1:5000/v3 --os-identity-api-version 3 role list") do |r|
|
||||
expect(r.stdout).to match(/admin/)
|
||||
expect(r.stderr).to be_empty
|
||||
end
|
||||
end
|
||||
it 'should find beaker endpoints' do
|
||||
shell("openstack #{auth_creds} --os-auth-url http://127.0.0.1:5000/v3 --os-identity-api-version 3 endpoint list") do |r|
|
||||
expect(r.stdout).to match(/1234/)
|
||||
expect(r.stderr).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
describe 'with v2 admin with v2 credentials' do
|
||||
include_examples 'keystone user/tenant/service/role/endpoint resources using v2 API',
|
||||
'--os-username admin --os-password a_big_secret --os-project-name openstack'
|
||||
end
|
||||
describe 'with v2 service with v2 credentials' do
|
||||
include_examples 'keystone user/tenant/service/role/endpoint resources using v2 API',
|
||||
'--os-username beaker-ci --os-password secret --os-project-name services'
|
||||
end
|
||||
describe 'with v2 admin with v3 credentials' do
|
||||
include_examples 'keystone user/tenant/service/role/endpoint resources using v3 API',
|
||||
'--os-username admin --os-password a_big_secret --os-project-name openstack --os-user-domain-name default_domain --os-project-domain-name default_domain'
|
||||
end
|
||||
describe "with v2 service with v3 credentials" do
|
||||
include_examples 'keystone user/tenant/service/role/endpoint resources using v3 API',
|
||||
'--os-username beaker-ci --os-password secret --os-project-name services --os-user-domain-name default_domain --os-project-domain-name default_domain'
|
||||
end
|
||||
describe 'with v3 admin with v3 credentials' do
|
||||
include_examples 'keystone user/tenant/service/role/endpoint resources using v3 API',
|
||||
'--os-username adminv3 --os-password a_big_secret --os-project-name openstackv3 --os-user-domain-name admin_domain --os-project-domain-name admin_domain'
|
||||
end
|
||||
describe "with v3 service with v3 credentials" do
|
||||
include_examples 'keystone user/tenant/service/role/endpoint resources using v3 API',
|
||||
'--os-username beaker-civ3 --os-password secret --os-project-name servicesv3 --os-user-domain-name service_domain --os-project-domain-name service_domain'
|
||||
end
|
||||
|
||||
end
|
||||
end
|
111
spec/classes/keystone_federation_identity_provider_spec.rb
Normal file
111
spec/classes/keystone_federation_identity_provider_spec.rb
Normal file
@ -0,0 +1,111 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'keystone::federation::identity_provider' do
|
||||
|
||||
let :pre_condition do
|
||||
"class { 'keystone':
|
||||
admin_tokend => 'dummy',
|
||||
service_name => 'httpd',
|
||||
enable_ssl=> true }"
|
||||
end
|
||||
|
||||
let :params do
|
||||
{ :user => 'keystone',
|
||||
:certfile => '/etc/keystone/ssl/certs/signing_cert.pem',
|
||||
:keyfile => '/etc/keystone/ssl/private/signing_key.pem',
|
||||
:idp_entity_id => 'https://keystone.example.com/v3/OS-FEDERATION/saml2/idp',
|
||||
:idp_sso_endpoint => 'https://keystone.example.com/v3/OS-FEDERATION/saml2/sso',
|
||||
:idp_metadata_path => '/etc/keystone/saml2_idp_metadata.xml' }
|
||||
end
|
||||
|
||||
let :optional_params do
|
||||
{ :idp_organization_name => 'ExampleCompany',
|
||||
:idp_organization_display_name => 'Example',
|
||||
:idp_organization_url => 'www.example.com',
|
||||
:idp_contact_company => 'someone',
|
||||
:idp_contact_name => 'name',
|
||||
:idp_contact_surname => 'surname',
|
||||
:idp_contact_email => 'name@example.com',
|
||||
:idp_contact_telephone => '+55000000000',
|
||||
:idp_contact_type => 'other' }
|
||||
end
|
||||
|
||||
shared_examples_for 'keystone federation identity provider' do
|
||||
|
||||
it { is_expected.to contain_class('keystone::params') }
|
||||
|
||||
context 'keystone not running under apache' do
|
||||
let :pre_condition do
|
||||
"class { 'keystone':
|
||||
admin_tokend => 'dummy',
|
||||
service_name => 'keystone',
|
||||
enable_ssl=> true }"
|
||||
end
|
||||
|
||||
it_raises 'a Puppet::Error', /Keystone need to be running under Apache for Federation work./
|
||||
end
|
||||
|
||||
it 'should have' do
|
||||
is_expected.to contain_package('xmlsec1').with(
|
||||
:ensure => 'present',
|
||||
)
|
||||
is_expected.to contain_package('python-pysaml2').with(
|
||||
:ensure => 'present',
|
||||
)
|
||||
end
|
||||
|
||||
it 'should configure keystone.conf' do
|
||||
is_expected.to contain_keystone_config('saml/certfile').with_value(params[:certfile])
|
||||
is_expected.to contain_keystone_config('saml/keyfile').with_value(params[:keyfile])
|
||||
is_expected.to contain_keystone_config('saml/idp_entity_id').with_value(params[:idp_entity_id])
|
||||
is_expected.to contain_keystone_config('saml/idp_sso_endpoint').with_value(params[:idp_sso_endpoint])
|
||||
is_expected.to contain_keystone_config('saml/idp_metadata_path').with_value(params[:idp_metadata_path])
|
||||
end
|
||||
|
||||
it { is_expected.to contain_exec('saml_idp_metadata').with(
|
||||
:command => "keystone-manage saml_idp_metadata > #{params[:idp_metadata_path]}",
|
||||
:creates => "#{params[:idp_metadata_path]}",
|
||||
) }
|
||||
|
||||
it 'creates saml idp metadata file' do
|
||||
is_expected.to contain_file("#{params[:idp_metadata_path]}").with(
|
||||
:ensure => 'present',
|
||||
:mode => '0600',
|
||||
:owner => 'keystone',
|
||||
)
|
||||
end
|
||||
|
||||
context 'configure Keystone with optional params' do
|
||||
before :each do
|
||||
params.merge!(optional_params)
|
||||
end
|
||||
|
||||
it 'should configure keystone.conf' do
|
||||
is_expected.to contain_keystone_config('saml/certfile').with_value(params[:certfile])
|
||||
is_expected.to contain_keystone_config('saml/keyfile').with_value(params[:keyfile])
|
||||
is_expected.to contain_keystone_config('saml/idp_entity_id').with_value(params[:idp_entity_id])
|
||||
is_expected.to contain_keystone_config('saml/idp_sso_endpoint').with_value(params[:idp_sso_endpoint])
|
||||
is_expected.to contain_keystone_config('saml/idp_metadata_path').with_value(params[:idp_metadata_path])
|
||||
is_expected.to contain_keystone_config('saml/idp_organization_name').with_value(params[:idp_organization_name])
|
||||
is_expected.to contain_keystone_config('saml/idp_organization_display_name').with_value(params[:idp_organization_display_name])
|
||||
is_expected.to contain_keystone_config('saml/idp_organization_url').with_value(params[:idp_organization_url])
|
||||
is_expected.to contain_keystone_config('saml/idp_contact_company').with_value(params[:idp_contact_company])
|
||||
is_expected.to contain_keystone_config('saml/idp_contact_name').with_value(params[:idp_contact_name])
|
||||
is_expected.to contain_keystone_config('saml/idp_contact_surname').with_value(params[:idp_contact_surname])
|
||||
is_expected.to contain_keystone_config('saml/idp_contact_email').with_value(params[:idp_contact_email])
|
||||
is_expected.to contain_keystone_config('saml/idp_contact_telephone').with_value(params[:idp_contact_telephone])
|
||||
is_expected.to contain_keystone_config('saml/idp_contact_type').with_value(params[:idp_contact_type])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid values for idp_contact_type' do
|
||||
before do
|
||||
params.merge!(:idp_contact_type => 'foobar')
|
||||
end
|
||||
|
||||
it_raises 'a Puppet::Error', /Allowed values for idp_contact_type are: technical, support, administrative, billing and other/
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue
Block a user