Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for httpd auth-api service for containers. #15881

Merged
merged 3 commits into from
Sep 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions app/models/authenticator/httpd.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,24 +108,36 @@ def user_details_from_headers(username, request)
end

def user_attrs_from_external_directory(username)
if MiqEnvironment::Command.is_container?
user_attrs_from_external_directory_via_auth_api(username)
else
user_attrs_from_external_directory_via_dbus(username)
end
end

ATTRS_NEEDED = %w(mail givenname sn displayname domainname).freeze

def user_attrs_from_external_directory_via_dbus(username)
return unless username
require "dbus"

attrs_needed = %w(mail givenname sn displayname domainname)

sysbus = DBus.system_bus
ifp_service = sysbus["org.freedesktop.sssd.infopipe"]
ifp_object = ifp_service.object("/org/freedesktop/sssd/infopipe")
ifp_object.introspect
ifp_interface = ifp_object["org.freedesktop.sssd.infopipe"]
begin
user_attrs = ifp_interface.GetUserAttr(username, attrs_needed).first
user_attrs = ifp_interface.GetUserAttr(username, ATTRS_NEEDED).first
rescue => err
raise _("Unable to get attributes for external user %{user_name} - %{error}") %
{:user_name => username, :error => err}
end

attrs_needed.each_with_object({}) { |attr, hash| hash[attr] = Array(user_attrs[attr]).first }
ATTRS_NEEDED.each_with_object({}) { |attr, hash| hash[attr] = Array(user_attrs[attr]).first }
end

def user_attrs_from_external_directory_via_auth_api(username)
HttpdAuthApi.new.user_attrs(username, ATTRS_NEEDED)
end
end
end
13 changes: 13 additions & 0 deletions app/models/miq_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@ def self.get_ldap_groups_by_user(user, bind_dn, bind_pwd)
end

def self.get_httpd_groups_by_user(user)
if MiqEnvironment::Command.is_container?
get_httpd_groups_by_user_via_auth_api(user)
else
get_httpd_groups_by_user_via_dbus(user)
end
end

def self.get_httpd_groups_by_user_via_dbus(user)
require "dbus"

username = user.kind_of?(self) ? user.userid : user
Expand All @@ -135,6 +143,11 @@ def self.get_httpd_groups_by_user(user)
strip_group_domains(user_groups.first)
end

def self.get_httpd_groups_by_user_via_auth_api(user)
groups = HttpdAuthApi.new.user_groups(user)
strip_group_domains(groups)
end

def get_filters(type = nil)
entitlement.try(:get_filters, type)
end
Expand Down
49 changes: 49 additions & 0 deletions lib/httpd_auth_api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require "faraday"
require "json"

class HttpdAuthApi
def initialize(options = {})
@options = options
end

def user_attrs(userid, attributes = nil)
if attributes.present?
auth_api("/api/user_attrs/#{userid}?attributes=#{attributes.join(',')}")
else
auth_api("/api/user_attrs/#{userid}")
end
end

def user_groups(userid)
auth_api("/api/user_groups/#{userid}")
end

private

def auth_api(request_url)
host = ENV["HTTPD_AUTH_API_SERVICE_HOST"]
port = ENV["HTTPD_AUTH_API_SERVICE_PORT"]
conn = Faraday.new(:url => "http://#{host}:#{port}") do |faraday|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this use the URI::HTTP.build?

faraday.options[:open_timeout] = @options[:open_timeout] || 5 # Net::HTTP open_timeout
faraday.options[:timeout] = @options[:timeout] || 30 # Net::HTTP read_timeout
faraday.request(:url_encoded) # form-encode POST params
faraday.adapter(Faraday.default_adapter) # make requests with Net::HTTP
end

begin
response = conn.run_request(:get, request_url, nil, nil) do |req|
req.headers[:content_type] = "application/json"
req.headers[:accept] = "application/json"
end
rescue => err
raise("Failed to query the httpd Authentication API service - #{err}")
end

if response.body
body = JSON.parse(response.body.strip)
end

raise(body["error"]) if response.status >= 400
body["result"]
end
end
66 changes: 66 additions & 0 deletions spec/lib/httpd_auth_api_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
require 'webmock/rspec'

RSpec.describe HttpdAuthApi do
let(:jdoe_userid) { "jdoe" }

let(:jdoe_user_attrs) do
{
"mail" => "[email protected]",
"givenname" => "John",
"sn" => "Doe",
"displayname" => "John Doe",
"domainname" => "acme.com"
}
end

let(:jdoe_user_groups) { %w(evmgroup-super_administrator evmgroup-user) }

let(:jim_userid) { "jim" }
let(:jim_attrs_error) { "Unable to get attributes for user #{jim_userid} - No such user" }
let(:jim_groups_error) { "Unable to get groups for user #{jim_userid} - No such user" }

before do
ENV["HTTPD_AUTH_API_SERVICE_HOST"] = "1.2.3.4"
ENV["HTTPD_AUTH_API_SERVICE_PORT"] = "3400"

stub_request(:get, "http://1.2.3.4:3400/api/user_attrs/#{jdoe_userid}")
.to_return(:status => 200, :body => { "result" => jdoe_user_attrs }.to_json)

stub_request(:get, "http://1.2.3.4:3400/api/user_attrs/#{jdoe_userid}?attributes=givenname,sn")
.to_return(:status => 200, :body => { "result" => jdoe_user_attrs.slice("givenname", "sn") }.to_json)

stub_request(:get, "http://1.2.3.4:3400/api/user_attrs/#{jim_userid}")
.to_return(:status => 400, :body => { "error" => jim_attrs_error }.to_json)

stub_request(:get, "http://1.2.3.4:3400/api/user_groups/#{jdoe_userid}")
.to_return(:status => 200, :body => { "result" => jdoe_user_groups }.to_json)

stub_request(:get, "http://1.2.3.4:3400/api/user_groups/#{jim_userid}")
.to_return(:status => 400, :body => { "error" => jim_groups_error }.to_json)
end

context "user_attrs" do
it "returns the result section of the response" do
expect(described_class.new.user_attrs(jdoe_userid)).to match(jdoe_user_attrs)
end

it "converts attribute list to comma separated attributes parameter" do
expect(described_class.new.user_attrs(jdoe_userid, %w(givenname sn)))
.to match(jdoe_user_attrs.slice("givenname", "sn"))
end

it "properly raises error messages" do
expect { described_class.new.user_attrs(jim_userid) }.to raise_error(jim_attrs_error)
end
end

context "user_groups" do
it "returns the result section of the response" do
expect(described_class.new.user_groups(jdoe_userid)).to match(jdoe_user_groups)
end

it "properly raises error messages" do
expect { described_class.new.user_groups(jim_userid) }.to raise_error(jim_groups_error)
end
end
end
6 changes: 3 additions & 3 deletions spec/models/authenticator/httpd_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ def authenticate
end
end

describe ".user_attrs_from_external_directory" do
describe ".user_attrs_from_external_directory_via_dbus" do
before do
require "dbus"
sysbus = double('sysbus')
Expand All @@ -414,7 +414,7 @@ def authenticate
end

it "should return nil for unspecified user" do
expect(subject.send(:user_attrs_from_external_directory, nil)).to be_nil
expect(subject.send(:user_attrs_from_external_directory_via_dbus, nil)).to be_nil
end

it "should return user attributes hash for valid user" do
Expand All @@ -434,7 +434,7 @@ def authenticate

allow(@ifp_interface).to receive(:GetUserAttr).with('jdoe', requested_attrs).and_return(jdoe_attrs)

expect(subject.send(:user_attrs_from_external_directory, 'jdoe')).to eq(expected_jdoe_attrs)
expect(subject.send(:user_attrs_from_external_directory_via_dbus, 'jdoe')).to eq(expected_jdoe_attrs)
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions spec/models/miq_group_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@

allow(@ifp_interface).to receive(:GetUserGroups).with('user').and_return(memberships)

expect(MiqGroup.get_httpd_groups_by_user('user')).to eq(memberships.first)
expect(MiqGroup.get_httpd_groups_by_user_via_dbus('user')).to eq(memberships.first)
end

it "should remove FQDN from the groups by user name with external authentication" do
Expand All @@ -89,7 +89,7 @@

allow(@ifp_interface).to receive(:GetUserGroups).with('user').and_return(ifp_memberships)

expect(MiqGroup.get_httpd_groups_by_user('user')).to eq(memberships.first)
expect(MiqGroup.get_httpd_groups_by_user_via_dbus('user')).to eq(memberships.first)
end
end

Expand Down