diff --git a/lib/puppet/provider/keycloak_client/kcadm.rb b/lib/puppet/provider/keycloak_client/kcadm.rb index 25e7682..853c246 100644 --- a/lib/puppet/provider/keycloak_client/kcadm.rb +++ b/lib/puppet/provider/keycloak_client/kcadm.rb @@ -1,448 +1,448 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'keycloak_api')) Puppet::Type.type(:keycloak_client).provide(:kcadm, parent: Puppet::Provider::KeycloakAPI) do desc '' mk_resource_methods def attributes_properties [ :login_theme, :access_token_lifespan, ] end def dot_attributes_properties [ :access_token_lifespan, ] end def self.auth_flow_properties { browser_flow: 'browser', direct_grant_flow: 'direct_grant', } end def auth_flow_properties self.class.auth_flow_properties end def attribute_key(property) if dot_attributes_properties.include?(property) property.to_s.tr('_', '.') else property end end def self.get_client_roles(realm, client) output = kcadm('get', "clients/#{client}/roles", realm) Puppet.debug("Client #{client} in realm #{realm} roles: #{output}") data = JSON.parse(output) roles = [] data.each do |d| # filter out 'uma_protection' client role created when # authorization_services_enabled property is set to true if !d['composite'] && d['name'] != 'uma_protection' roles.push(d['name']) end end Puppet.debug("Returned client roles: #{roles}") roles end def get_client_roles(*args) self.class.get_client_roles(*args) end def self.instances clients = [] realms.each do |realm| output = kcadm('get', 'clients', realm) Puppet.debug("#{realm} clients: #{output}") begin data = JSON.parse(output) rescue JSON::ParserError Puppet.debug('Unable to parse output from kcadm get clients') data = [] end data.each do |d| # avoid built-in clients if d.key?('clientAuthenticatorType') && d['clientAuthenticatorType'] == 'client-secret' && !d.key?('name') begin secret_output = kcadm('get', "clients/#{d['id']}/client-secret", realm) rescue Puppet.debug("Unable to get clients/#{d['id']}/client-secret") secret_output = '{}' end secret_data = JSON.parse(secret_output) secret = secret_data['value'] else secret = nil end client = {} client[:ensure] = :present client[:id] = d['id'] client[:client_id] = d['clientId'] client[:realm] = realm client[:name] = "#{client[:client_id]} on #{client[:realm]}" type_properties.each do |property| next if [:roles].include?(property) camel_key = camelize(property) dot_key = property.to_s.tr('_', '.') key = property.to_s attributes = d['attributes'] || {} auth_flows = d['authenticationFlowBindingOverrides'] || {} if property == :secret value = secret elsif d.key?(camel_key) value = d[camel_key] elsif attributes.key?(dot_key) value = attributes[dot_key] elsif attributes.key?(key) value = attributes[key] elsif auth_flows.key?(auth_flow_properties[property]) flow_alias = flow_ids(realm)[auth_flows[auth_flow_properties[property]]] value = flow_alias end if !!value == value # rubocop:disable Style/DoubleNegation value = value.to_s.to_sym end client[property.to_sym] = value end # The absence of a value should be 'absent' client[:login_theme] = 'absent' if client[:login_theme].nil? client[:roles] = get_client_roles(realm, client[:id]) clients << new(client) end end clients end def self.prefetch(resources) clients = instances resources.keys.each do |name| provider = clients.find { |c| c.client_id == resources[name][:client_id] && c.realm == resources[name][:realm] } if provider resources[name].provider = provider end end end def scope_map return @scope_map if @scope_map output = kcadm('get', 'client-scopes', resource[:realm], nil, ['id', 'name']) begin data = JSON.parse(output) rescue JSON::ParserError Puppet.debug('Unable to parse output from kcadm get client-scopes') return {} end @scope_map = {} data.each do |d| @scope_map[d['name']] = d['id'] end @scope_map end def self.flow_ids(realm) @flow_ids = {} unless @flow_ids return @flow_ids[realm] if @flow_ids[realm] output = kcadm('get', 'authentication/flows', realm, nil, ['id', 'alias']) begin data = JSON.parse(output) rescue JSON::ParserError Puppet.debug('Unable to parse output from kcadm get authentication/flows') return {} end @flow_ids[realm] = {} data.each do |d| @flow_ids[realm][d['alias']] = d['id'] @flow_ids[realm][d['id']] = d['alias'] end @flow_ids[realm] end def flow_ids @flow_ids = {} unless @flow_ids return @flow_ids unless @flow_ids.empty? self.class.instance_variable_set(:@flow_ids, nil) @flow_ids = self.class.flow_ids(resource[:realm]) @flow_ids end def create raise(Puppet::Error, "Realm is mandatory for #{resource.type} #{resource.name}") if resource[:realm].nil? data = {} data[:id] = resource[:id] data[:clientId] = resource[:client_id] data[:secret] = resource[:secret] if resource[:secret] type_properties.each do |property| next if [:default_client_scopes, :optional_client_scopes, :roles].include?(property) next unless resource[property.to_sym] value = convert_property_value(resource[property.to_sym]) next if value == 'absent' || value == :absent || value.nil? if attributes_properties.include?(property) unless data.key?(:attributes) data[:attributes] = {} end data[:attributes][attribute_key(property)] = value elsif auth_flow_properties.include?(property) unless data.key?(:authenticationFlowBindingOverrides) data[:authenticationFlowBindingOverrides] = {} end flow_id = flow_ids[value] data[:authenticationFlowBindingOverrides][auth_flow_properties[property]] = flow_id else data[camelize(property)] = value end end t = Tempfile.new('keycloak_client') t.write(JSON.pretty_generate(data)) t.close Puppet.debug(IO.read(t.path)) begin - if resource[:login_theme] + if resource[:login_theme] && resource[:login_theme].to_sym != :absent check_theme_exists(resource[:login_theme], "Keycloak_client[#{resource[:name]}]") end output = kcadm('create', 'clients', resource[:realm], t.path) Puppet.debug("create client output: #{output}") rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm create client failed\nError message: #{e.message}" end if resource[:default_client_scopes] || resource[:optional_client_scopes] client = JSON.parse(output) scope_id = nil end if resource[:default_client_scopes] remove_default_scopes = client['defaultClientScopes'] - resource[:default_client_scopes] begin remove_default_scopes.each do |s| scope_id = scope_map[s] kcadm('delete', "clients/#{resource[:id]}/default-client-scopes/#{scope_id}", resource[:realm]) end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm delete clients/#{resource[:id]}/default-client-scopes/#{scope_id}: #{e.message}" end end if resource[:optional_client_scopes] remove_optional_scopes = client['optionalClientScopes'] - resource[:optional_client_scopes] begin remove_optional_scopes.each do |s| scope_id = scope_map[s] kcadm('delete', "clients/#{resource[:id]}/optional-client-scopes/#{scope_id}", resource[:realm]) end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm delete clients/#{resource[:id]}/optional-client-scopes/#{scope_id}: #{e.message}" end end if resource[:default_client_scopes] add_default_scopes = resource[:default_client_scopes] - client['defaultClientScopes'] begin add_default_scopes.each do |s| scope_id = scope_map[s] kcadm('update', "clients/#{resource[:id]}/default-client-scopes/#{scope_id}", resource[:realm]) end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm update clients/#{resource[:id]}/default-client-scopes/#{scope_id}: #{e.message}" end end if resource[:optional_client_scopes] add_optional_scopes = resource[:optional_client_scopes] - client['optionalClientScopes'] begin add_optional_scopes.each do |s| scope_id = scope_map[s] kcadm('update', "clients/#{resource[:id]}/optional-client-scopes/#{scope_id}", resource[:realm]) end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm update clients/#{resource[:id]}/optional-client-scopes/#{scope_id}: #{e.message}" end end role = nil if resource[:roles] roles = get_client_roles(resource[:realm], resource[:id]) remove_roles = roles - resource[:roles] begin remove_roles.each do |s| role = s kcadm('delete', "clients/#{resource[:id]}/roles/#{role}", resource[:realm]) end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm delete realms/#{resource[:realm]}/clients/#{resource[:id]}/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_client_role') role_data_t.write(JSON.pretty_generate(role_data)) role_data_t.close Puppet.debug(IO.read(role_data_t.path)) kcadm('create', "clients/#{resource[:id]}/roles", resource[:realm], role_data_t.path) end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm create realms/#{resource[:realm]}/clients/#{resource[:id]}/roles/#{role}: #{e.message}" end end @property_hash[:ensure] = :present end def destroy raise(Puppet::Error, "Realm is mandatory for #{resource.type} #{resource.name}") if resource[:realm].nil? begin kcadm('delete', "clients/#{id}", resource[:realm]) 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? raise(Puppet::Error, "Realm is mandatory for #{resource.type} #{resource.name}") if resource[:realm].nil? data = {} data[:clientId] = resource[:client_id] data[:authenticationFlowBindingOverrides] = {} type_properties.each do |property| next if [:default_client_scopes, :optional_client_scopes, :roles].include?(property) next unless @property_flush[property.to_sym] value = convert_property_value(@property_flush[property.to_sym]) value = nil if value.to_s == 'absent' if attributes_properties.include?(property) unless data.key?(:attributes) data[:attributes] = {} end data[:attributes][attribute_key(property)] = value elsif auth_flow_properties.include?(property) flow_id = value.nil? ? nil : flow_ids[value] data[:authenticationFlowBindingOverrides][auth_flow_properties[property]] = flow_id else data[camelize(property)] = value end end # Keycload API requires "serviceAccountsEnabled": true to be present in # the JSON when "authorizationServicesEnabled": true if data['authorizationServicesEnabled'] && data['serviceAccountsEnabled'].nil? data[:serviceAccountsEnabled] = true end # Only update if more than clientId set if data.keys.size > 1 t = Tempfile.new('keycloak_client') t.write(JSON.pretty_generate(data)) t.close Puppet.debug(IO.read(t.path)) begin - if @property_flush[:login_theme] + if @property_flush[:login_theme] && @property_flush[:login_theme].to_sym != :absent check_theme_exists(@property_flush[:login_theme], "Keycloak_client[#{resource[:name]}]") end kcadm('update', "clients/#{id}", resource[:realm], t.path) rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm update client failed\nError message: #{e.message}" end end if @property_flush[:default_client_scopes] || @property_flush[:optional_client_scopes] scope_id = nil end if @property_flush[:default_client_scopes] remove_default_scopes = @property_hash[:default_client_scopes] - @property_flush[:default_client_scopes] begin remove_default_scopes.each do |s| scope_id = scope_map[s] kcadm('delete', "clients/#{id}/default-client-scopes/#{scope_id}", resource[:realm]) end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm delete clients/#{id}/default-client-scopes/#{scope_id}: #{e.message}" end end if @property_flush[:optional_client_scopes] remove_optional_scopes = @property_hash[:optional_client_scopes] - @property_flush[:optional_client_scopes] begin remove_optional_scopes.each do |s| scope_id = scope_map[s] kcadm('delete', "clients/#{id}/optional-client-scopes/#{scope_id}", resource[:realm]) end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm delete clients/#{id}/optional-client-scopes/#{scope_id}: #{e.message}" end end if @property_flush[:default_client_scopes] add_default_scopes = @property_flush[:default_client_scopes] - @property_hash[:default_client_scopes] begin add_default_scopes.each do |s| scope_id = scope_map[s] kcadm('update', "clients/#{id}/default-client-scopes/#{scope_id}", resource[:realm]) end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm update clients/#{id}/default-client-scopes/#{scope_id}: #{e.message}" end end if @property_flush[:optional_client_scopes] add_optional_scopes = @property_flush[:optional_client_scopes] - @property_hash[:optional_client_scopes] begin add_optional_scopes.each do |s| scope_id = scope_map[s] kcadm('update', "clients/#{id}/optional-client-scopes/#{scope_id}", resource[:realm]) end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm update clients/#{id}/optional-client-scopes/#{scope_id}: #{e.message}" end end role = nil if @property_flush[:roles] remove_roles = @property_hash[:roles] - @property_flush[:roles] begin remove_roles.each do |s| role = s kcadm('delete', "clients/#{resource[:id]}/roles/#{role}", resource[:realm]) end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm delete realms/#{resource[:realm]}/clients/#{resource[:id]}/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_client_role') role_data_t.write(JSON.pretty_generate(role_data)) role_data_t.close Puppet.debug(IO.read(role_data_t.path)) kcadm('create', "clients/#{resource[:id]}/roles", resource[:realm], role_data_t.path) end rescue Puppet::ExecutionFailure => e raise Puppet::Error, "kcadm create realms/#{resource[:realm]}/clients/#{resource[:id]}/roles/#{role}: #{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_client.rb b/lib/puppet/type/keycloak_client.rb index d6fba2d..86f2d7f 100644 --- a/lib/puppet/type/keycloak_client.rb +++ b/lib/puppet/type/keycloak_client.rb @@ -1,260 +1,260 @@ require_relative '../../puppet_x/keycloak/type' require_relative '../../puppet_x/keycloak/array_property' Puppet::Type.newtype(:keycloak_client) do desc <<-DESC Manage Keycloak clients @example Add a OpenID Connect client keycloak_client { 'www.example.com': ensure => 'present', realm => 'test', redirect_uris => [ "https://www.example.com/oidc", "https://www.example.com", ], default_client_scopes => ['profile','email'], secret => 'supersecret', } DESC extend PuppetX::Keycloak::Type add_autorequires ensurable newparam(:name, namevar: true) do desc 'The client name' end newparam(:client_id, namevar: true) do desc 'clientId. Defaults to `name`.' defaultto do @resource[:name] end end newparam(:id) do desc 'Id. Defaults to `client_id`' defaultto do @resource[:client_id] end end newparam(:realm, namevar: true) do desc 'realm' end newparam(:secret) do desc 'secret' def change_to_s(currentvalue, _newvalue) if currentvalue == :absent 'created secret' else 'changed secret' end end def is_to_s(_currentvalue) # rubocop:disable Style/PredicateName '[old secret redacted]' end def should_to_s(_newvalue) '[new secret redacted]' end end newproperty(:protocol) do desc 'protocol' defaultto('openid-connect') newvalues('openid-connect', 'saml') munge { |v| v } end newproperty(:client_authenticator_type) do desc 'clientAuthenticatorType' defaultto 'client-secret' end newproperty(:default_client_scopes, array_matching: :all, parent: PuppetX::Keycloak::ArrayProperty) do desc 'defaultClientScopes' defaultto [] end newproperty(:optional_client_scopes, array_matching: :all, parent: PuppetX::Keycloak::ArrayProperty) do desc 'optionalClientScopes' defaultto [] end newproperty(:full_scope_allowed, boolean: true) do desc 'fullScopeAllowed' newvalues(:true, :false) defaultto(:true) end newproperty(:enabled, boolean: true) do desc 'enabled' newvalues(:true, :false) defaultto :true end newproperty(:standard_flow_enabled, boolean: true) do desc 'standardFlowEnabled' newvalues(:true, :false) defaultto :true end newproperty(:implicit_flow_enabled, boolean: true) do desc 'implicitFlowEnabled' newvalues(:true, :false) defaultto :false end newproperty(:direct_access_grants_enabled, boolean: true) do desc 'enabled' newvalues(:true, :false) defaultto :true end newproperty(:service_accounts_enabled, boolean: true) do desc 'serviceAccountsEnabled' newvalues(:true, :false) defaultto :false end newproperty(:authorization_services_enabled, boolean: true) do desc 'authorizationServicesEnabled' newvalues(:true, :false) defaultto :false # If authorizationServicesEnabled is set to false it will not be present in # "get client/" output. Puppet will thus see it as "absent". # This custom insync? implementation prevents Puppet from trying to set # the property to false on every run. def insync?(is) if is == :true && resource[:authorization_services_enabled] == :true true elsif is == :absent && resource[:authorization_services_enabled] == :false true else false end end end newproperty(:public_client, boolean: true) do desc 'enabled' newvalues(:true, :false) defaultto :false end newproperty(:root_url) do desc 'rootUrl' end newproperty(:redirect_uris, array_matching: :all, parent: PuppetX::Keycloak::ArrayProperty) do desc 'redirectUris' defaultto [] end newproperty(:base_url) do desc 'baseUrl' end newproperty(:web_origins, array_matching: :all, parent: PuppetX::Keycloak::ArrayProperty) do desc 'webOrigins' defaultto [] end newproperty(:login_theme) do desc 'login_theme' - defaultto 'absent' + defaultto :absent end newproperty(:access_token_lifespan) do desc 'access.token.lifespan' end newproperty(:browser_flow) do desc 'authenticationFlowBindingOverrides.browser (Use flow alias, not ID)' defaultto :absent end newproperty(:direct_grant_flow) do desc 'authenticationFlowBindingOverrides.direct_grant (Use flow alias, not ID)' defaultto :absent end newproperty(:roles, array_matching: :all, parent: PuppetX::Keycloak::ArrayProperty) do desc 'roles' defaultto [] end autorequire(:keycloak_client_scope) do requires = [] catalog.resources.each do |resource| next unless resource.class.to_s == 'Puppet::Type::Keycloak_client_scope' if self[:default_client_scopes].include?(resource[:resource_name]) requires << resource.name end if self[:optional_client_scopes].include?(resource[:resource_name]) requires << resource.name end end requires end autorequire(:keycloak_protocol_mapper) do requires = [] catalog.resources.each do |resource| next unless resource.class.to_s == 'Puppet::Type::Keycloak_protocol_mapper' if self[:default_client_scopes].include?(resource[:client_scope]) requires << resource.name end if self[:optional_client_scopes].include?(resource[:client_scope]) requires << resource.name end end requires end autorequire(:keycloak_flow) do requires = [] catalog.resources.each do |resource| next unless resource.class.to_s == 'Puppet::Type::Keycloak_flow' next if self[:realm] != resource[:realm] if self[:browser_flow] == resource[:alias] requires << resource.name end if self[:direct_grant_flow] == resource[:alias] requires << resource.name end end requires end validate do if self[:authorization_services_enabled] == :true && self[:service_accounts_enabled] == :false raise "Keycloak_client[#{self[:name]}] must have service_accounts_enabled => true if authorization_services_enabled => true" end end def self.title_patterns [ [ %r{^((\S+) on (\S+))$}, [ [:name], [:client_id], [:realm], ], ], [ %r{(.*)}, [ [:name], ], ], ] end end diff --git a/spec/unit/puppet/type/keycloak_client_spec.rb b/spec/unit/puppet/type/keycloak_client_spec.rb index 84d283e..b687883 100644 --- a/spec/unit/puppet/type/keycloak_client_spec.rb +++ b/spec/unit/puppet/type/keycloak_client_spec.rb @@ -1,222 +1,222 @@ require 'spec_helper' describe Puppet::Type.type(:keycloak_client) do let(:default_config) do { name: 'foo', realm: '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('foo') end it 'has client_id default to name' do expect(resource[:client_id]).to eq('foo') end it 'has id default to name' do expect(resource[:id]).to eq('foo') end it 'has realm' do expect(resource[:realm]).to eq('test') end it 'handles componsite name' do component = described_class.new(name: 'foo on test') expect(component[:name]).to eq('foo on test') expect(component[:client_id]).to eq('foo') expect(component[:realm]).to eq('test') end it 'defaults to client_authenticator_type=client-secret' do expect(resource[:client_authenticator_type]).to eq('client-secret') end it 'defaults to protocol=openid-connect' do expect(resource[:protocol]).to eq('openid-connect') end it 'does not allow invalid protocol' do config[:protocol] = 'foo' expect { resource }.to raise_error(%r{foo}) end defaults = { enabled: :true, standard_flow_enabled: :true, implicit_flow_enabled: :false, direct_access_grants_enabled: :true, service_accounts_enabled: :false, public_client: :false, full_scope_allowed: :true, default_client_scopes: [], optional_client_scopes: [], redirect_uris: [], web_origins: [], - login_theme: 'absent', + login_theme: :absent, browser_flow: :absent, direct_grant_flow: :absent, } describe 'basic properties' do # Test basic properties [ :secret, :login_theme, :root_url, :base_url, :browser_flow, :direct_grant_flow, ].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 'boolean properties' do # Test boolean properties [ :enabled, :standard_flow_enabled, :implicit_flow_enabled, :direct_access_grants_enabled, :service_accounts_enabled, :public_client, :full_scope_allowed, ].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, :redirect_uris, :web_origins, :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 it 'autorequires keycloak_realm' do keycloak_realm = Puppet::Type.type(:keycloak_realm).new(name: 'test') catalog = Puppet::Resource::Catalog.new catalog.add_resource resource catalog.add_resource keycloak_realm rel = resource.autorequire[0] expect(rel.source.ref).to eq(keycloak_realm.ref) expect(rel.target.ref).to eq(resource.ref) end it 'autorequires keycloak_client_scope' do config[:default_client_scopes] = ['foo'] keycloak_client_scope = Puppet::Type.type(:keycloak_client_scope).new(name: 'foo', realm: 'test') catalog = Puppet::Resource::Catalog.new catalog.add_resource resource catalog.add_resource keycloak_client_scope rel = resource.autorequire[0] expect(rel.source.ref).to eq(keycloak_client_scope.ref) expect(rel.target.ref).to eq(resource.ref) end it 'autorequires client_scope protocol mappers' do config[:default_client_scopes] = ['foo'] keycloak_protocol_mapper = Puppet::Type.type(:keycloak_protocol_mapper).new(name: 'bar', realm: 'test', client_scope: 'foo') catalog = Puppet::Resource::Catalog.new catalog.add_resource resource catalog.add_resource keycloak_protocol_mapper rel = resource.autorequire[0] expect(rel.source.ref).to eq(keycloak_protocol_mapper.ref) expect(rel.target.ref).to eq(resource.ref) end it 'autorequires browser flow' do config[:browser_flow] = 'foo' flow = Puppet::Type.type(:keycloak_flow).new(name: 'foo', realm: 'test') catalog = Puppet::Resource::Catalog.new catalog.add_resource resource catalog.add_resource flow rel = resource.autorequire[0] expect(rel.source.ref).to eq(flow.ref) expect(rel.target.ref).to eq(resource.ref) end end