Skip to content

Commit 7e3b200

Browse files
authored
Merge pull request #23723 from jrafanie/fix-freeipa-userid-email-creating-different-users
Fix freeipa userid or email logins creating different users
2 parents 045b8de + b0b827e commit 7e3b200

File tree

2 files changed

+89
-32
lines changed

2 files changed

+89
-32
lines changed

app/models/authenticator/httpd.rb

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ def find_userid_as_distinguished_name(user_attrs)
123123
User.in_my_region.where("userid LIKE ?", "%=#{user_attrs[:username]},%,#{dn_domain}").last
124124
end
125125

126+
# TODO: If we now have the principal name via krbPrincipalName, we can use that instead of this method.
127+
# In order to remove this method, we'd need to make sure all identity providers using httpd expose the principal name in the same format, even if it's just the username.
126128
def username_to_upn_name(user_attrs)
127129
return user_attrs[:username] if user_attrs[:domain].nil?
128130

@@ -133,7 +135,8 @@ def username_to_upn_name(user_attrs)
133135

134136
def user_details_from_external_directory(username)
135137
ext_user_attrs = user_attrs_from_external_directory(username)
136-
user_attrs = {:username => username,
138+
principal_username = ext_user_attrs["krbPrincipalName"] && normalize_username(ext_user_attrs["krbPrincipalName"])
139+
user_attrs = {:username => principal_username || username,
137140
:fullname => ext_user_attrs["displayname"],
138141
:firstname => ext_user_attrs["givenname"],
139142
:lastname => ext_user_attrs["sn"],
@@ -153,7 +156,8 @@ def user_details_from_headers(username, request)
153156

154157
delimiter = self.class.group_delimiter || user_headers['X-REMOTE-USER-GROUP-DELIMITER'].presence || /[;:,]/
155158
groups = CGI.unescape(user_headers['X-REMOTE-USER-GROUPS'] || '').split(delimiter)
156-
user_attrs = {:username => username,
159+
principal_username = user_headers['X-REMOTE-USER-PRINCIPAL'] && normalize_username(user_headers['X-REMOTE-USER-PRINCIPAL'])
160+
user_attrs = {:username => principal_username || username,
157161
:fullname => user_headers['X-REMOTE-USER-FULLNAME'],
158162
:firstname => user_headers['X-REMOTE-USER-FIRSTNAME'],
159163
:lastname => user_headers['X-REMOTE-USER-LASTNAME'],
@@ -164,6 +168,8 @@ def user_details_from_headers(username, request)
164168
end
165169

166170
private def user_headers_from_request(request)
171+
# NOTE: X-REMOTE-USER-PRINCIPAL must be set in the headers by the sssd/apache configuration
172+
# with a value of the krbPrincipalName or some other reasonable value based on the identity provider.
167173
%w[
168174
X-REMOTE-USER
169175
X-REMOTE-USER-FULLNAME
@@ -173,6 +179,7 @@ def user_details_from_headers(username, request)
173179
X-REMOTE-USER-DOMAIN
174180
X-REMOTE-USER-GROUPS
175181
X-REMOTE-USER-GROUP-DELIMITER
182+
X-REMOTE-USER-PRINCIPAL
176183
].each_with_object({}) do |k, h|
177184
h[k] = request.headers[k]&.force_encoding("UTF-8")
178185
end.delete_nils
@@ -198,7 +205,7 @@ def user_attrs_from_external_directory(username)
198205
end
199206
end
200207

201-
ATTRS_NEEDED = %w[mail givenname sn displayname domainname].freeze
208+
ATTRS_NEEDED = %w[mail givenname sn displayname domainname krbPrincipalName].freeze
202209

203210
def user_attrs_from_external_directory_via_dbus(username)
204211
return unless username

spec/models/authenticator/httpd_spec.rb

Lines changed: 79 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,19 @@
9393
{
9494
'X-Remote-User' => 'cheshire',
9595
'X-Remote-User-FullName' => 'Cheshire Cat',
96-
'X-Remote-User-FirstName' => 'Chechire',
96+
'X-Remote-User-FirstName' => 'Cheshire',
9797
'X-Remote-User-LastName' => 'Cat',
9898
'X-Remote-User-Email' => 'cheshire@example.com',
9999
'X-Remote-User-Domain' => 'example.com',
100100
'X-Remote-User-Groups' => 'wibble@fqdn:bubble@fqdn',
101101
}
102102
end
103103

104+
it "finds the user based on the downcased X-Remote-User-Principal header" do
105+
headers['X-Remote-User-Principal'] = 'cheshire@EXAMPLE.COM'
106+
expect(subject.lookup_by_identity('baduser', request)).to eq(cheshire)
107+
end
108+
104109
it "Handles missing request parameter" do
105110
expect(subject.lookup_by_identity('alice')).to eq(alice)
106111
end
@@ -125,22 +130,22 @@
125130
describe '#find_or_initialize_user' do
126131
let(:user_attrs_simple) do
127132
{:username => "sal",
128-
:fullname => "Test User Sal",
129-
:firstname => "Salvadore",
130-
:lastname => "Bigs",
131-
:email => "sal_email@example.com",
132-
:domain => "example.com"}
133+
:fullname => "Test User Sal",
134+
:firstname => "Salvadore",
135+
:lastname => "Bigs",
136+
:email => "sal_email@example.com",
137+
:domain => "example.com"}
133138
end
134139

135140
let(:identity_simple) { [user_attrs_simple, %w[mumble bumble bee]] }
136141

137142
let(:user_attrs_upn) do
138143
{:username => "sal@example.com",
139-
:fullname => "Test User Sal",
140-
:firstname => "Salvadore",
141-
:lastname => "Bigs",
142-
:email => "sal_email@example.com",
143-
:domain => "example.com"}
144+
:fullname => "Test User Sal",
145+
:firstname => "Salvadore",
146+
:lastname => "Bigs",
147+
:email => "sal_email@example.com",
148+
:domain => "example.com"}
144149
end
145150

146151
let(:identity_upn) { [user_attrs_upn, %w[mumble bumble bee]] }
@@ -644,7 +649,7 @@ def authenticate
644649
end
645650
end
646651

647-
describe ".user_attrs_from_external_directory_via_dbus" do
652+
context "with dbus mocks" do
648653
let(:ifp_interface) { double('ifp_interface') }
649654
before do
650655
require "dbus"
@@ -659,28 +664,73 @@ def authenticate
659664
allow(ifp_object).to receive(:[]).with("org.freedesktop.sssd.infopipe").and_return(ifp_interface)
660665
end
661666

662-
it "should return nil for unspecified user" do
663-
expect(subject.send(:user_attrs_from_external_directory_via_dbus, nil)).to be_nil
664-
end
667+
describe ".user_attrs_from_external_directory_via_dbus" do
668+
it "should return nil for unspecified user" do
669+
expect(subject.send(:user_attrs_from_external_directory_via_dbus, nil)).to be_nil
670+
end
671+
672+
it "should return user attributes hash for valid user" do
673+
requested_attrs = %w[mail givenname sn displayname domainname krbPrincipalName]
665674

666-
it "should return user attributes hash for valid user" do
667-
requested_attrs = %w[mail givenname sn displayname domainname]
675+
jdoe_attrs = [{"mail" => ["jdoe@example.com"],
676+
"givenname" => ["John"],
677+
"sn" => ["Doe"],
678+
"displayname" => ["John Doe"],
679+
"domainname" => ["ipa.test"],
680+
"krbPrincipalName" => ["jdoe@IPA.TEST"]}]
681+
682+
expected_jdoe_attrs = {"mail" => "jdoe@example.com",
683+
"givenname" => "John",
684+
"sn" => "Doe",
685+
"displayname" => "John Doe",
686+
"domainname" => "ipa.test",
687+
"krbPrincipalName" => "jdoe@IPA.TEST"}
688+
689+
allow(ifp_interface).to receive(:GetUserAttr).with('jdoe', requested_attrs).and_return(jdoe_attrs)
690+
691+
result = subject.send(:user_attrs_from_external_directory_via_dbus, 'jdoe')
692+
expect(result).to eq(expected_jdoe_attrs)
693+
end
668694

669-
jdoe_attrs = [{"mail" => ["jdoe@example.com"],
670-
"givenname" => ["John"],
671-
"sn" => ["Doe"],
672-
"displayname" => ["John Doe"],
673-
"domainname" => ["example.com"]}]
695+
it "should handle missing krbPrincipalName gracefully" do
696+
requested_attrs = %w[mail givenname sn displayname domainname krbPrincipalName]
674697

675-
expected_jdoe_attrs = {"mail" => "jdoe@example.com",
676-
"givenname" => "John",
677-
"sn" => "Doe",
678-
"displayname" => "John Doe",
679-
"domainname" => "example.com"}
698+
jdoe_attrs = [{"mail" => ["jdoe@example.com"],
699+
"givenname" => ["John"],
700+
"sn" => ["Doe"],
701+
"displayname" => ["John Doe"],
702+
"domainname" => ["ipa.test"]}]
680703

681-
allow(ifp_interface).to receive(:GetUserAttr).with('jdoe', requested_attrs).and_return(jdoe_attrs)
704+
expected_jdoe_attrs = {"mail" => "jdoe@example.com",
705+
"givenname" => "John",
706+
"sn" => "Doe",
707+
"displayname" => "John Doe",
708+
"domainname" => "ipa.test",
709+
"krbPrincipalName" => nil}
682710

683-
expect(subject.send(:user_attrs_from_external_directory_via_dbus, 'jdoe')).to eq(expected_jdoe_attrs)
711+
allow(ifp_interface).to receive(:GetUserAttr).with('jdoe', requested_attrs).and_return(jdoe_attrs)
712+
713+
expect(subject.send(:user_attrs_from_external_directory_via_dbus, 'jdoe')).to eq(expected_jdoe_attrs)
714+
end
715+
end
716+
717+
describe "#user_details_from_external_directory" do
718+
it "should normalize krbPrincipalName when present" do
719+
requested_attrs = %w[mail givenname sn displayname domainname krbPrincipalName]
720+
721+
jdoe_attrs = [{"mail" => ["jdoe@example.com"],
722+
"givenname" => ["John"],
723+
"sn" => ["Doe"],
724+
"displayname" => ["John Doe"],
725+
"domainname" => ["ipa.test"],
726+
"krbPrincipalName" => ["jdoe@IPA.TEST"]}]
727+
728+
allow(ifp_interface).to receive(:GetUserAttr).with('jdoe', requested_attrs).and_return(jdoe_attrs)
729+
allow(MiqGroup).to receive(:get_httpd_groups_by_user).with('jdoe').and_return([])
730+
731+
user_attrs, _groups = subject.send(:user_details_from_external_directory, 'jdoe')
732+
expect(user_attrs[:username]).to eq("jdoe@ipa.test")
733+
end
684734
end
685735
end
686736
end

0 commit comments

Comments
 (0)