diff --git a/lib/puppet/provider/keycloak_realm/kcadm.rb b/lib/puppet/provider/keycloak_realm/kcadm.rb index 0c71f25..b685cc1 100644 --- a/lib/puppet/provider/keycloak_realm/kcadm.rb +++ b/lib/puppet/provider/keycloak_realm/kcadm.rb @@ -1,457 +1,457 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'keycloak_api')) Puppet::Type.type(:keycloak_realm).provide(:kcadm, parent: Puppet::Provider::KeycloakAPI) do desc '' mk_resource_methods def flow_properties [ :browser_flow, :registration_flow, :direct_grant_flow, :reset_credentials_flow, :client_authentication_flow, :docker_authentication_flow, ] end def self.smtp_server_properties [ :smtp_server_user, :smtp_server_password, :smtp_server_host, :smtp_server_port, :smtp_server_auth, :smtp_server_starttls, :smtp_server_ssl, :smtp_server_envelope_from, :smtp_server_from, :smtp_server_from_display_name, :smtp_server_reply_to, :smtp_server_reply_to_display_name, ] end def self.browser_security_headers [ :content_security_policy, ] end def self.get_client_scopes(realm, type) output = kcadm('get', "realms/#{realm}/default-#{type}-client-scopes") Puppet.debug("Realms #{realm} #{type} client scopes: #{output}") data = JSON.parse(output) scopes = {} data.each do |d| scopes[d['name']] = d['id'] end Puppet.debug("Returned scopes: #{scopes}") scopes end def get_client_scopes(*args) self.class.get_client_scopes(*args) end def self.get_realm_roles(realm) output = kcadm('get', 'roles', realm) Puppet.debug("Realms #{realm} roles: #{output}") data = JSON.parse(output) roles = [] data.each do |d| # filter out 'create-realm' role from master realm as it should not be removed if !d['composite'] && d['name'] != 'create-realm' roles.push(d['name']) end end Puppet.debug("Returned roles: #{roles}") roles end def get_realm_roles(*args) self.class.get_realm_roles(*args) end def self.get_events_config(realm) output = kcadm('get', 'events/config', realm) Puppet.debug("#{realm} events/config: #{output}") begin data = JSON.parse(output) rescue JSON::ParserError Puppet.debug('Unable to parse output from kcadm get events/config') data = {} end data.delete('enabledEventTypes') data end def available_flows(realm) output = kcadm('get', 'authentication/flows', realm, nil, ['alias']) Puppet.debug("#{realm} authentication/flows: #{output}") begin data = JSON.parse(output) rescue JSON::ParserError Puppet.debug('Unable to parse output from kcadm get authentication/flows') return [] end data.map { |f| f['alias'] } end def self.instances realms = [] output = kcadm('get', 'realms') Puppet.debug("Realms: #{output}") begin data = JSON.parse(output) rescue JSON::ParserError Puppet.debug('Unable to parse output from kcadm get realms') data = [] end data.each do |d| realm = {} realm[:ensure] = :present realm[:id] = d['id'] realm[:name] = d['realm'] events_config = get_events_config(d['realm']) type_properties.each do |property| next if [:default_client_scopes, :optional_client_scopes, :roles].include?(property) value = if property.to_s =~ %r{events} events_config[camelize(property)] elsif browser_security_headers.include?(property) d['browserSecurityHeaders'][camelize(property)] elsif smtp_server_properties.include?(property) d['smtpServer'][camelize(property.to_s.gsub(%r{smtp_server_}, ''))] else d[camelize(property)] end if !!value == value # rubocop:disable Style/DoubleNegation value = value.to_s.to_sym end realm[property.to_sym] = value end default_scopes = get_client_scopes(realm[:name], 'default') realm[:default_client_scopes] = default_scopes.keys.map { |k| k.to_s } optional_scopes = get_client_scopes(realm[:name], 'optional') realm[:optional_client_scopes] = optional_scopes.keys.map { |k| k.to_s } realm[:roles] = get_realm_roles(realm[:name]) realms << new(realm) end realms end def self.prefetch(resources) realms = instances resources.keys.each do |name| provider = realms.find { |realm| realm.name == name } if provider resources[name].provider = provider end end end def create data = {} events_config = {} data[:id] = resource[:id] data[:realm] = resource[:name] type_properties.each do |property| next if flow_properties.include?(property) next if [:default_client_scopes, :optional_client_scopes, :roles].include?(property) if self.class.browser_security_headers.include?(property) && !data.key?('browserSecurityHeaders') data['browserSecurityHeaders'] = {} end if self.class.smtp_server_properties.include?(property) && !data.key?('smtpServer') data['smtpServer'] = {} end if property.to_s =~ %r{events} events_config[camelize(property)] = convert_property_value(resource[property.to_sym]) elsif resource[property.to_sym] if self.class.browser_security_headers.include?(property) data['browserSecurityHeaders'][camelize(property)] = convert_property_value(resource[property.to_sym]) elsif self.class.smtp_server_properties.include?(property) && resource[property] data['smtpServer'][camelize(property.to_s.gsub(%r{smtp_server_}, ''))] = resource[property] else data[camelize(property)] = convert_property_value(resource[property.to_sym]) end end end t = Tempfile.new('keycloak_realm') t.write(JSON.pretty_generate(data)) t.close Puppet.debug(IO.read(t.path)) begin [ :login_theme, :account_theme, :admin_theme, :email_theme, ].each do |theme| if resource[theme] check_theme_exists(resource[theme], "Keycloak_realm[#{resource[:name]}]") end end kcadm('create', 'realms', nil, t.path) rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm create realm failed\nError message: #{e.message}" end scope_id = nil if resource[:default_client_scopes] default_scopes = default_scopes ||= get_client_scopes(resource[:name], 'default') remove_default_scopes = default_scopes.keys - resource[:default_client_scopes] begin remove_default_scopes.each do |s| scope_id = default_scopes[s] kcadm('delete', "realms/#{resource[:name]}/default-default-client-scopes/#{scope_id}") end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm delete realms/#{resource[:name]}/default-default-client-scopes/#{scope_id}: #{e.message}" end end if resource[:optional_client_scopes] optional_scopes = optional_scopes ||= get_client_scopes(resource[:name], 'optional') remove_optional_scopes = optional_scopes.keys - resource[:optional_client_scopes] begin remove_optional_scopes.each do |s| scope_id = optional_scopes[s] kcadm('delete', "realms/#{resource[:name]}/default-optional-client-scopes/#{scope_id}") end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm delete realms/#{resource[:name]}/default-optional-client-scopes/#{scope_id}: #{e.message}" end end if resource[:default_client_scopes] default_scopes = default_scopes ||= get_client_scopes(resource[:name], 'default') add_default_scopes = resource[:default_client_scopes] - default_scopes.keys begin add_default_scopes.each do |s| scope_id = default_scopes[s] kcadm('update', "realms/#{resource[:name]}/default-default-client-scopes/#{scope_id}") end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm update realms/#{resource[:name]}/default-default-client-scopes/#{scope_id}: #{e.message}" end end if resource[:optional_client_scopes] optional_scopes = optional_scopes ||= get_client_scopes(resource[:name], 'optional') add_optional_scopes = resource[:optional_client_scopes] - optional_scopes.keys begin add_optional_scopes.each do |s| scope_id = optional_scopes[s] kcadm('update', "realms/#{resource[:name]}/default-optional-client-scopes/#{scope_id}") end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm update realms/#{resource[:name]}/default-optional-client-scopes/#{scope_id}: #{e.message}" end end role = nil - if resource[:roles] + if resource[:roles] && resource[:manage_roles].to_s == 'true' roles = get_realm_roles(resource[:name]) remove_roles = roles - resource[:roles] begin remove_roles.each do |s| role = s kcadm('delete', "roles/#{role}", resource[:name]) end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm delete realms/#{resource[:name]}/roles/#{role}: #{e.message}" end add_roles = resource[:roles] - roles begin add_roles.each do |s| role = s role_data = { 'description' => "${role_#{role}}", 'name' => role } role_data_t = Tempfile.new('keycloak_realm_role') role_data_t.write(JSON.pretty_generate(role_data)) role_data_t.close Puppet.debug(IO.read(role_data_t.path)) kcadm('create', 'roles', resource[:name], role_data_t.path) end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm create realms/#{resource[:name]}/roles/#{role}: #{e.message}" end end unless events_config.empty? events_config_t = Tempfile.new('keycloak_events_config') events_config_t.write(JSON.pretty_generate(events_config)) events_config_t.close Puppet.debug(IO.read(events_config_t.path)) begin kcadm('update', 'events/config', resource[:name], events_config_t.path) rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm update events config failed\nError message: #{e.message}" end end @property_hash[:ensure] = :present end def destroy begin kcadm('delete', "realms/#{resource[:name]}") rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm delete realm failed\nError message: #{e.message}" end @property_hash.clear end def exists? @property_hash[:ensure] == :present end def initialize(value = {}) super(value) @property_flush = {} end type_properties.each do |prop| define_method "#{prop}=".to_sym do |value| @property_flush[prop] = value end end def flush unless @property_flush.empty? data = {} events_config = {} type_properties.each do |property| next if [:default_client_scopes, :optional_client_scopes, :roles].include?(property) if flow_properties.include?(property) && !available_flows(resource[:name]).include?(resource[property.to_sym]) Puppet.warning("Keycloak_realm[#{resource[:name]}]: #{property} '#{resource[property.to_sym]}' does not exist, skipping") next end if self.class.browser_security_headers.include?(property) && !data.key?('browserSecurityHeaders') data['browserSecurityHeaders'] = {} end if self.class.smtp_server_properties.include?(property) && !data.key?('smtpServer') data['smtpServer'] = {} end if @property_flush[property.to_sym] || resource[property.to_sym] if self.class.browser_security_headers.include?(property) data['browserSecurityHeaders'][camelize(property)] = convert_property_value(resource[property.to_sym]) elsif self.class.smtp_server_properties.include?(property) && resource[property] data['smtpServer'][camelize(property.to_s.gsub(%r{smtp_server_}, ''))] = resource[property] else data[camelize(property)] = convert_property_value(resource[property.to_sym]) end end if property.to_s =~ %r{events} events_config[camelize(property)] = convert_property_value(resource[property.to_sym]) end end unless data.empty? t = Tempfile.new('keycloak_realm') t.write(JSON.pretty_generate(data)) t.close Puppet.debug(IO.read(t.path)) begin [ :login_theme, :account_theme, :admin_theme, :email_theme, ].each do |theme| if @property_flush[theme] check_theme_exists(@property_flush[theme], "Keycloak_realm[#{resource[:name]}]") end end kcadm('update', "realms/#{resource[:name]}", nil, t.path) rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm update realm failed\nError message: #{e.message}" end end scope_id = nil if @property_flush[:default_client_scopes] default_scopes = default_scopes ||= get_client_scopes(resource[:name], 'default') remove_default_scopes = default_scopes.keys - @property_flush[:default_client_scopes] begin remove_default_scopes.each do |s| scope_id = default_scopes[s] kcadm('delete', "realms/#{resource[:name]}/default-default-client-scopes/#{scope_id}") end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm delete realms/#{resource[:name]}/default-default-client-scopes/#{scope_id}: #{e.message}" end end if @property_flush[:optional_client_scopes] optional_scopes = optional_scopes ||= get_client_scopes(resource[:name], 'optional') remove_optional_scopes = optional_scopes.keys - @property_flush[:optional_client_scopes] begin remove_optional_scopes.each do |s| scope_id = optional_scopes[s] kcadm('delete', "realms/#{resource[:name]}/default-optional-client-scopes/#{scope_id}") end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm delete realms/#{resource[:name]}/default-optional-client-scopes/#{scope_id}: #{e.message}" end end if @property_flush[:default_client_scopes] default_scopes = default_scopes ||= get_client_scopes(resource[:name], 'default') add_default_scopes = @property_flush[:default_client_scopes] - default_scopes.keys begin add_default_scopes.each do |s| scope_id = default_scopes[s] kcadm('update', "realms/#{resource[:name]}/default-default-client-scopes/#{scope_id}") end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm update realms/#{resource[:name]}/default-default-client-scopes/#{scope_id}: #{e.message}" end end if @property_flush[:optional_client_scopes] optional_scopes = optional_scopes ||= get_client_scopes(resource[:name], 'optional') add_optional_scopes = @property_flush[:optional_client_scopes] - optional_scopes.keys begin add_optional_scopes.each do |s| scope_id = optional_scopes[s] kcadm('update', "realms/#{resource[:name]}/default-optional-client-scopes/#{scope_id}") end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm update realms/#{resource[:name]}/default-optional-client-scopes/#{scope_id}: #{e.message}" end end role = nil - if @property_flush[:roles] + if @property_flush[:roles] && resource[:manage_roles].to_s == 'true' remove_roles = @property_hash[:roles] - @property_flush[:roles] begin remove_roles.each do |s| role = s kcadm('delete', "roles/#{role}", resource[:name]) end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm delete realms/#{resource[:name]}/roles/#{role}: #{e.message}" end add_roles = @property_flush[:roles] - @property_hash[:roles] begin add_roles.each do |s| role = s role_data = { 'description' => "${role_#{role}}", 'name' => role } role_data_t = Tempfile.new('keycloak_realm_role') role_data_t.write(JSON.pretty_generate(role_data)) role_data_t.close Puppet.debug(IO.read(role_data_t.path)) kcadm('create', 'roles', resource[:name], role_data_t.path) end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm create realms/#{resource[:name]}/roles/#{role}: #{e.message}" end end unless events_config.empty? events_config_t = Tempfile.new('keycloak_events_config') events_config_t.write(JSON.pretty_generate(events_config)) events_config_t.close Puppet.debug(IO.read(events_config_t.path)) begin kcadm('update', 'events/config', resource[:name], events_config_t.path) rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm update events config failed\nError message: #{e.message}" end end end # Collect the resources again once they've been changed (that way `puppet # resource` will show the correct values after changes have been made). @property_hash = resource.to_hash end end diff --git a/lib/puppet/type/keycloak_realm.rb b/lib/puppet/type/keycloak_realm.rb index 47cf21b..a800e54 100644 --- a/lib/puppet/type/keycloak_realm.rb +++ b/lib/puppet/type/keycloak_realm.rb @@ -1,289 +1,302 @@ require_relative '../../puppet_x/keycloak/type' require_relative '../../puppet_x/keycloak/array_property' require_relative '../../puppet_x/keycloak/integer_property' Puppet::Type.newtype(:keycloak_realm) do desc <<-DESC Manage Keycloak realms @example Add a realm with a custom theme keycloak_realm { 'test': ensure => 'present', remember_me => true, login_with_email_allowed => false, login_theme => 'my_theme', } DESC extend PuppetX::Keycloak::Type add_autorequires(false) ensurable newparam(:name, namevar: true) do desc 'The realm name' end newparam(:id) do desc 'Id. Default to `name`.' defaultto do @resource[:name] end end newproperty(:display_name) do desc 'displayName' end newproperty(:display_name_html) do desc 'displayNameHtml' end newproperty(:login_theme) do desc 'loginTheme' defaultto 'keycloak' end newproperty(:account_theme) do desc 'accountTheme' defaultto 'keycloak' end newproperty(:admin_theme) do desc 'adminTheme' defaultto 'keycloak' end newproperty(:email_theme) do desc 'emailTheme' defaultto 'keycloak' end newproperty(:internationalization_enabled, boolean: true) do desc 'internationalizationEnabled' newvalues(:true, :false) defaultto :false end newproperty(:sso_session_idle_timeout_remember_me, parent: PuppetX::Keycloak::IntegerProperty) do desc 'ssoSessionIdleTimeoutRememberMe' end newproperty(:sso_session_max_lifespan_remember_me, parent: PuppetX::Keycloak::IntegerProperty) do desc 'ssoSessionMaxLifespanRememberMe' end newproperty(:sso_session_idle_timeout, parent: PuppetX::Keycloak::IntegerProperty) do desc 'ssoSessionIdleTimeout' end newproperty(:sso_session_max_lifespan, parent: PuppetX::Keycloak::IntegerProperty) do desc 'ssoSessionMaxLifespan' end newproperty(:access_code_lifespan, parent: PuppetX::Keycloak::IntegerProperty) do desc 'accessCodeLifespan' end newproperty(:access_code_lifespan_user_action, parent: PuppetX::Keycloak::IntegerProperty) do desc 'accessCodeLifespanUserAction' end newproperty(:access_token_lifespan, parent: PuppetX::Keycloak::IntegerProperty) do desc 'accessTokenLifespan' end newproperty(:access_token_lifespan_for_implicit_flow, parent: PuppetX::Keycloak::IntegerProperty) do desc 'accessTokenLifespanForImplicitFlow' end newproperty(:enabled, boolean: true) do desc 'enabled' newvalues(:true, :false) defaultto :true end newproperty(:remember_me, boolean: true) do desc 'rememberMe' newvalues(:true, :false) defaultto :false end newproperty(:registration_allowed, boolean: true) do desc 'registrationAllowed' newvalues(:true, :false) defaultto :false end newproperty(:login_with_email_allowed, boolean: true) do desc 'loginWithEmailAllowed' newvalues(:true, :false) defaultto :true end newproperty(:reset_password_allowed, boolean: true) do desc 'resetPasswordAllowed' newvalues(:true, :false) defaultto :false end newproperty(:verify_email, boolean: true) do desc 'verifyEmail' newvalues(:true, :false) defaultto :false end newproperty(:browser_flow) do desc 'browserFlow' defaultto('browser') munge { |v| v.to_s } end newproperty(:registration_flow) do desc 'registrationFlow' defaultto('registration') munge { |v| v.to_s } end newproperty(:direct_grant_flow) do desc 'directGrantFlow' defaultto('direct grant') munge { |v| v.to_s } end newproperty(:reset_credentials_flow) do desc 'resetCredentialsFlow' defaultto('reset credentials') munge { |v| v.to_s } end newproperty(:client_authentication_flow) do desc 'clientAuthenticationFlow' defaultto('clients') munge { |v| v.to_s } end newproperty(:docker_authentication_flow) do desc 'dockerAuthenticationFlow' defaultto('docker auth') munge { |v| v.to_s } end newproperty(:default_client_scopes, array_matching: :all, parent: PuppetX::Keycloak::ArrayProperty) do desc 'Default Client Scopes' end newproperty(:optional_client_scopes, array_matching: :all, parent: PuppetX::Keycloak::ArrayProperty) do desc 'Optional Client Scopes' end newproperty(:supported_locales, array_matching: :all, parent: PuppetX::Keycloak::ArrayProperty) do desc 'Supported Locales' end newproperty(:content_security_policy) do desc 'contentSecurityPolicy' defaultto("frame-src 'self'; frame-ancestors 'self'; object-src 'none';") munge { |v| v.to_s } end newproperty(:events_enabled, boolean: true) do desc 'eventsEnabled' newvalues(:true, :false) defaultto :false end newproperty(:events_expiration) do desc 'eventsExpiration' end newproperty(:events_listeners, array_matching: :all, parent: PuppetX::Keycloak::ArrayProperty) do desc 'eventsListeners' defaultto ['jboss-logging'] end newproperty(:admin_events_enabled, boolean: true) do desc 'adminEventsEnabled' newvalues(:true, :false) defaultto :false end newproperty(:admin_events_details_enabled, boolean: true) do desc 'adminEventsDetailsEnabled' newvalues(:true, :false) defaultto :false end newproperty(:smtp_server_user) do desc 'smtpServer user' end newproperty(:smtp_server_password) do desc 'smtpServer password' def insync?(is) if is =~ %r{^[\*]+$} Puppet.warning("Property 'smtp_server_password' is set and Puppet has no way to check current value") true else false end end def should_to_s(_newvalue) '[new smtp_server_password redacted]' end end newproperty(:smtp_server_host) do desc 'smtpServer host' end newproperty(:smtp_server_port, parent: PuppetX::Keycloak::IntegerProperty) do desc 'smtpServer port' end newproperty(:smtp_server_auth, boolean: true) do desc 'smtpServer auth' newvalues(:true, :false) end newproperty(:smtp_server_starttls, boolean: true) do desc 'smtpServer starttls' newvalues(:true, :false) end newproperty(:smtp_server_ssl, boolean: true) do desc 'smtpServer ssl' newvalues(:true, :false) end newproperty(:smtp_server_from) do desc 'smtpServer from' end newproperty(:smtp_server_envelope_from) do desc 'smtpServer envelope_from' end newproperty(:smtp_server_from_display_name) do desc 'smtpServer fromDisplayName' end newproperty(:smtp_server_reply_to) do desc 'smtpServer replyto' end newproperty(:smtp_server_reply_to_display_name) do desc 'smtpServer replyToDisplayName' end newproperty(:brute_force_protected, boolean: true) do desc 'bruteForceProtected' newvalues(:true, :false) end + newparam(:manage_roles, boolean: true) do + desc 'Manage realm roles' + newvalues(:true, :false) + defaultto(:true) + end + newproperty(:roles, array_matching: :all, parent: PuppetX::Keycloak::ArrayProperty) do desc 'roles' defaultto ['offline_access', 'uma_authorization'] + + def insync?(is) + if resource[:manage_roles].to_s == 'false' + return true + end + super(is) + end end end diff --git a/spec/unit/puppet/type/keycloak_realm_spec.rb b/spec/unit/puppet/type/keycloak_realm_spec.rb index 98aeee7..3b41de7 100644 --- a/spec/unit/puppet/type/keycloak_realm_spec.rb +++ b/spec/unit/puppet/type/keycloak_realm_spec.rb @@ -1,200 +1,201 @@ require 'spec_helper' describe Puppet::Type.type(:keycloak_realm) do let(:default_config) do { name: 'test', } end let(:config) do default_config end let(:resource) do described_class.new(config) end it 'adds to catalog without raising an error' do catalog = Puppet::Resource::Catalog.new expect { catalog.add_resource resource }.not_to raise_error end it 'has a name' do expect(resource[:name]).to eq('test') end it 'has id default to name' do expect(resource[:id]).to eq('test') end defaults = { login_theme: 'keycloak', account_theme: 'keycloak', admin_theme: 'keycloak', email_theme: 'keycloak', access_code_lifespan_user_action: nil, access_token_lifespan_for_implicit_flow: nil, enabled: :true, remember_me: :false, login_with_email_allowed: :true, browser_flow: 'browser', registration_flow: 'registration', direct_grant_flow: 'direct grant', reset_credentials_flow: 'reset credentials', client_authentication_flow: 'clients', docker_authentication_flow: 'docker auth', content_security_policy: "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", events_enabled: :false, events_listeners: ['jboss-logging'], admin_events_enabled: :false, admin_events_details_enabled: :false, } describe 'basic properties' do # Test basic properties [ :display_name, :display_name_html, :login_theme, :account_theme, :admin_theme, :email_theme, :events_expiration, :browser_flow, :registration_flow, :direct_grant_flow, :reset_credentials_flow, :client_authentication_flow, :docker_authentication_flow, :content_security_policy, :smtp_server_user, :smtp_server_password, :smtp_server_host, :smtp_server_envelope_from, :smtp_server_from, :smtp_server_from_display_name, :smtp_server_reply_to, :smtp_server_reply_to_display_name, ].each do |p| it "should accept a #{p}" do config[p] = 'foo' expect(resource[p]).to eq('foo') end next unless defaults[p] it "should have default for #{p}" do expect(resource[p]).to eq(defaults[p]) end end end describe 'integer properties' do # Test integer properties [ :sso_session_idle_timeout_remember_me, :sso_session_max_lifespan_remember_me, :sso_session_idle_timeout, :sso_session_max_lifespan, :access_code_lifespan, :access_code_lifespan_user_action, :access_token_lifespan, :access_token_lifespan_for_implicit_flow, :smtp_server_port, ].each do |p| it "should accept a #{p}" do config[p] = 100 expect(resource[p]).to eq(100) end next unless defaults[p] it "should have default for #{p}" do expect(resource[p]).to eq(defaults[p]) end end end describe 'boolean properties' do # Test boolean properties [ :remember_me, :registration_allowed, :reset_password_allowed, :verify_email, :login_with_email_allowed, :internationalization_enabled, + :manage_roles, :events_enabled, :admin_events_enabled, :admin_events_details_enabled, :smtp_server_auth, :smtp_server_starttls, :smtp_server_ssl, :brute_force_protected, ].each do |p| it "should accept true for #{p}" do config[p] = true expect(resource[p]).to eq(:true) end it "should accept true for #{p} string" do config[p] = 'true' expect(resource[p]).to eq(:true) end it "should accept false for #{p}" do config[p] = false expect(resource[p]).to eq(:false) end it "should accept false for #{p} string" do config[p] = 'false' expect(resource[p]).to eq(:false) end it "should not accept strings for #{p}" do config[p] = 'foo' expect { resource }.to raise_error(%r{foo}) end next unless defaults[p] it "should have default for #{p}" do expect(resource[p]).to eq(defaults[p]) end end end describe 'array properties' do # Array properties [ :default_client_scopes, :optional_client_scopes, :events_listeners, :supported_locales, :roles, ].each do |p| it "should accept array for #{p}" do config[p] = ['foo', 'bar'] expect(resource[p]).to eq(['foo', 'bar']) end next unless defaults[p] it "should have default for #{p}" do expect(resource[p]).to eq(defaults[p]) end end end it 'autorequires keycloak_conn_validator' do keycloak_conn_validator = Puppet::Type.type(:keycloak_conn_validator).new(name: 'keycloak') catalog = Puppet::Resource::Catalog.new catalog.add_resource resource catalog.add_resource keycloak_conn_validator rel = resource.autorequire[0] expect(rel.source.ref).to eq(keycloak_conn_validator.ref) expect(rel.target.ref).to eq(resource.ref) end it 'autorequires kcadm-wrapper.sh' do file = Puppet::Type.type(:file).new(name: 'kcadm-wrapper.sh', path: '/opt/keycloak/bin/kcadm-wrapper.sh') catalog = Puppet::Resource::Catalog.new catalog.add_resource resource catalog.add_resource file rel = resource.autorequire[0] expect(rel.source.ref).to eq(file.ref) expect(rel.target.ref).to eq(resource.ref) end end