Project

General

Profile

Download (4.24 KB) Statistics
| Branch: | Tag: | Revision:
require 'gssapi'
require 'proxy/kerberos'
require 'uri'
require 'xmlrpc/client'
require 'net/https'

module Proxy::FreeIPARealm
class Provider
include Proxy::Log
include Proxy::Util
include Proxy::Kerberos

attr_reader :remove_dns, :ipa_config

def initialize(ipa_config, keytab_path, principal, remove_dns)
@ipa_config = ipa_config
@keytab_path = keytab_path
@principal = principal
@remove_dns = remove_dns
end

def ipa
@ipa ||= configure_ipa
end

def configure_ipa
# Get krb5 token
init_krb5_ccache @keytab_path, @principal
gssapi = GSSAPI::Simple.new(ipa_config.host, "HTTP")
token = gssapi.init_context

login = Net::HTTP.new(ipa_config.host, 443)
login.use_ssl = true
login.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Post.new("/ipa/session/login_kerberos", 'Authorization'=>"Negotiate #{strict_encode64(token)}", 'Referer' => ipa_config.uri)
response = login.request(request)
cookie = response['Set-Cookie']

# FreeIPA API returns some nils, Ruby XML-RPC doesn't like this
XMLRPC::Config.module_eval { const_set(:ENABLE_NIL_PARSER, true) }
ipa = XMLRPC::Client.new2(ipa_config.scheme + "://" + ipa_config.host + "/ipa/session/xml")
# For some reason ipa insists on having 'Referer' header to be present...
ipa.http_header_extra={ 'Referer' => ipa_config.uri, 'Content-Type' => 'text/xml; charset=utf-8' }
ipa.cookie = cookie # set the session cookie

ipa
end

def check_realm(realm)
raise Exception.new "Unknown realm #{realm}" unless realm.casecmp(ipa_config.realm).zero?
end

def find(hostname)
ipa_call("host_show", [hostname])
rescue XMLRPC::FaultException => e
if e.message =~ /not found/
nil
else
raise
end
end

def create(realm, hostname, params)
check_realm realm

# Send params to FreeIPA, may want to send more than one in the future
setattr = params.has_key?('userclass') ? ["userclass=#{params['userclass']}"] : []

host = find(hostname)
if host.nil?
result = do_host_create(hostname, setattr)
elsif params[:rebuild] == "true"
result = do_host_rebuild(hostname, setattr, host["result"]["has_keytab"])
else
result = do_host_modify(hostname, setattr)
end

JSON.pretty_generate(result["result"])
rescue => e
if e.message =~ /no modifications/
JSON.pretty_generate("message" => "nothing to do")
else
raise
end
end

def do_host_rebuild(hostname, setattr, has_keytab)
options = {:random => 1}
options[:setattr] = setattr unless setattr.nil?

ipa_call("host_disable", [hostname]) if has_keytab
ipa_call('host_mod', [hostname], options)
end

def do_host_modify(hostname, setattr)
options = {}
options[:setattr] = setattr unless setattr.nil?
ipa_call('host_mod', [hostname], options)
end

def do_host_create(hostname, setattr)
options = {:random => 1, :force => 1}
options[:setattr] = setattr unless setattr.nil?
ipa_call('host_add', [hostname], options)
end

def delete(realm, hostname)
check_realm realm
begin
result = ipa_call("host_del", [hostname], "updatedns" => remove_dns)
rescue
if remove_dns
# If the host doesn't have a DNS record (e.g. deleting a system in Foreman before it's built)
# the above call will fail. Try again with updatedns => false
result = ipa_call("host_del", [hostname], "updatedns" => false)
else
raise
end
end
JSON.pretty_generate(result)
end

def self.ensure_utf(object)
case object
when String
if object.respond_to?(:force_encoding)
object.dup.force_encoding('UTF-8')
else
object
end
when Hash
object.reduce({}) do |h, (key, val)|
h.update(ensure_utf(key) => ensure_utf(val))
end
when Array
object.map { |val| ensure_utf(val) }
else
object
end
end

private

def ipa_call(*args)
logger.debug "Making IPA call: #{args}"
self.class.ensure_utf(ipa.call(*args))
end
end
end
(3-3/5)