Skip to content

Commit

Permalink
Resolves merge conflict in OmniAuth::Strategies::CAS::SamlTicketValid…
Browse files Browse the repository at this point in the history
…ator. Adds #success_body loading in OmniAuth::Strategies::CAS::SamlTicketValidator
  • Loading branch information
redconfetti committed Nov 18, 2023
1 parent b734118 commit be56dcb
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 20 deletions.
51 changes: 31 additions & 20 deletions lib/omniauth/strategies/cas/saml_ticket_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ class CAS
class SamlTicketValidator
VALIDATION_REQUEST_HEADERS = { 'Accept' => '*/*' }

attr_reader :success_body

# Build a validator from a +configuration+, a
# +return_to+ URL, and a +ticket+.
#
Expand All @@ -23,6 +25,7 @@ def initialize(strategy, options, return_to_url, ticket)
# Executes a network request to process the CAS Service Response
def call
@response_body = get_saml_response_body
@success_body = find_authentication_success(@response_body)
self
end

Expand All @@ -40,7 +43,6 @@ def user_info
doc.remove_namespaces!
if success?(doc)
attrs = extract_attributes(doc)
<<<<<<< HEAD
attrs["nameIdentifier"] = extract_name_identifier(doc)
{ "user" => attrs["uid"] }.merge(attrs)
else
Expand All @@ -49,17 +51,29 @@ def user_info
end
rescue Nokogiri::XML::XPath::SyntaxError
OmniAuth.logger.warn "Could not parse SAML response, will return nil user_info:\n#{@response_body}"
=======
{ "user" => attrs["uid"] }.merge(attrs)
end
rescue Nokogiri::XML::XPath::SyntaxError
>>>>>>> f87f036 (create SamlTicketValidator for validating tickets against samlValidate and asserting attributes)
nil
end
end

private

# finds an `<cas:authenticationSuccess>` node in
# a `<cas:serviceResponse>` body if present; returns nil
# if the passed body is nil or if there is no such node.
def find_authentication_success(body)
return nil if body.nil? || body == ''
begin
doc = Nokogiri::XML(body)
begin
doc.xpath('/Envelope/Body/Response/Status/StatusCode')
rescue Nokogiri::XML::XPath::SyntaxError
nil
end
rescue Nokogiri::XML::XPath::SyntaxError
nil
end
end

def success?(doc)
doc.css("StatusCode").attr("Value").text == "saml1p:Success"
end
Expand All @@ -71,26 +85,23 @@ def extract_attributes(doc)
end
end

<<<<<<< HEAD
def extract_name_identifier(doc)
doc.css("AuthenticationStatement Subject NameIdentifier").text
end

=======
>>>>>>> f87f036 (create SamlTicketValidator for validating tickets against samlValidate and asserting attributes)
def saml_payload
<<-SAML
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<samlp:Request xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" MajorVersion="1"
MinorVersion="1" RequestID="#{SecureRandom.uuid}" IssueInstant="#{Time.now.to_s}">
<samlp:AssertionArtifact>
#{@ticket}
</samlp:AssertionArtifact>
</samlp:Request>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<samlp:Request xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" MajorVersion="1"
MinorVersion="1" RequestID="#{SecureRandom.uuid}" IssueInstant="#{Time.now.to_s}">
<samlp:AssertionArtifact>
#{@ticket}
</samlp:AssertionArtifact>
</samlp:Request>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
SAML
end

Expand Down
78 changes: 78 additions & 0 deletions spec/fixtures/berkeley_cas_success.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<Response InResponseTo="clc.example.com"
IssueInstant="2023-11-17T21:55:49.424Z"
MajorVersion="1"
MinorVersion="1"
ResponseID="_fcf43fe7a22512b96095b4a368b26a5f"
xmlns:saml1p="urn:oasis:names:tc:SAML:1.0:protocol"
>
<Status>
<StatusCode Value="saml1p:Success" />
</Status>
<Assertion
AssertionID="_c2373528d5183c5a981561dc3baf145b"
IssueInstant="2023-11-17T21:55:49.424Z"
Issuer="localhost"
MajorVersion="1"
MinorVersion="1"
xmlns:saml1="urn:oasis:names:tc:SAML:1.0:assertion"
>
<Conditions NotBefore="2023-11-17T21:55:49.424Z" NotOnOrAfter="2023-11-17T21:56:49.424Z">
<AudienceRestrictionCondition>
<Audience>https://clc.example.com/auth/cas/callback?url=https%3A%2F%2Fclc.example.com%2F</Audience>
</AudienceRestrictionCondition>
</Conditions>
<AuthenticationStatement
AuthenticationInstant="2023-11-17T21:56:19.066Z"
AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:password"
>
<Subject>
<NameIdentifier>1044957</NameIdentifier>
<SubjectConfirmation>
<ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:artifact</ConfirmationMethod>
</SubjectConfirmation>
</Subject>
</AuthenticationStatement>
<AttributeStatement>
<Subject>
<NameIdentifier>1044957</NameIdentifier>
<SubjectConfirmation>
<ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:artifact</ConfirmationMethod>
</SubjectConfirmation>
</Subject>
<Attribute AttributeName="credentialType" AttributeNamespace="http://www.ja-sig.org/products/cas/">
<AttributeValue>UsernamePasswordCredential</AttributeValue>
</Attribute>
<Attribute AttributeName="clientIpAddress" AttributeNamespace="http://www.ja-sig.org/products/cas/">
<AttributeValue>192.168.0.5</AttributeValue>
</Attribute>
<Attribute AttributeName="samlAuthenticationStatementAuthMethod" AttributeNamespace="http://www.ja-sig.org/products/cas/">
<AttributeValue>urn:oasis:names:tc:SAML:1.0:am:password</AttributeValue>
</Attribute>
<Attribute AttributeName="isFromNewLogin" AttributeNamespace="http://www.ja-sig.org/products/cas/">
<AttributeValue>true</AttributeValue>
</Attribute>
<Attribute AttributeName="authenticationDate" AttributeNamespace="http://www.ja-sig.org/products/cas/">
<AttributeValue>2023-11-17T21:56:19.066445Z</AttributeValue>
</Attribute>
<Attribute AttributeName="authenticationMethod" AttributeNamespace="http://www.ja-sig.org/products/cas/">
<AttributeValue>Static Credentials</AttributeValue>
</Attribute>
<Attribute AttributeName="successfulAuthenticationHandlers" AttributeNamespace="http://www.ja-sig.org/products/cas/">
<AttributeValue>Static Credentials</AttributeValue>
</Attribute>
<Attribute AttributeName="serverIpAddress" AttributeNamespace="http://www.ja-sig.org/products/cas/">
<AttributeValue>192.168.0.45</AttributeValue>
</Attribute>
<Attribute AttributeName="userAgent" AttributeNamespace="http://www.ja-sig.org/products/cas/">
<AttributeValue>Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36</AttributeValue>
</Attribute>
<Attribute AttributeName="longTermAuthenticationRequestTokenUsed" AttributeNamespace="http://www.ja-sig.org/products/cas/">
<AttributeValue>false</AttributeValue>
</Attribute>
</AttributeStatement>
</Assertion>
</Response>
</Body>
</Envelope>
113 changes: 113 additions & 0 deletions spec/omniauth/strategies/cas/saml_ticket_validator_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
require 'spec_helper'

describe OmniAuth::Strategies::CAS::SamlTicketValidator do
let(:strategy) do
double('strategy',
service_validate_url: 'https://example.org/serviceValidate'
)
end
let(:provider_options) do
double('provider_options',
disable_ssl_verification?: false,
merge_multivalued_attributes: false,
ca_path: '/etc/ssl/certsZOMG'
)
end
let(:validator) do
OmniAuth::Strategies::CAS::SamlTicketValidator.new( strategy, provider_options, '/foo', nil )
end

describe '#call' do
before do
stub_request(:post, 'https://example.org/serviceValidate?')
.to_return(status: 200, body: '')
end

subject { validator.call }

it 'returns itself' do
expect(subject).to eq validator
end

it 'uses the configured CA path' do
subject
expect(provider_options).to have_received :ca_path
end
end

describe 'called instances' do
let(:ok_fixture) do
File.expand_path(File.join(File.dirname(__FILE__), '../../../fixtures/berkeley_cas_success.xml'))
end
let(:service_response) { File.read(ok_fixture) }

describe '#success_body' do
before do
stub_request(:post, 'https://example.org/serviceValidate?')
.to_return(status: 200, body: service_response)
validator.call
end

subject { validator.success_body }

it 'provides status code' do
expect(subject).to be_an_instance_of Nokogiri::XML::NodeSet
expect(subject.first).to be_an_instance_of Nokogiri::XML::Element
expect(subject.first['Value']).to eq 'saml1p:Success'
end
end

describe '#user_info' do
before do
stub_request(:post, 'https://example.org/serviceValidate?')
.to_return(status: 200, body: service_response)
validator.call
end

subject { validator.user_info }

context 'with default settings' do
it 'parses user info from the response' do
expect(subject).to include 'authenticationDate' => '2023-11-17T21:56:19.066445Z'
expect(subject).to include 'authenticationMethod' => 'Static Credentials'
expect(subject).to include 'clientIpAddress' => '192.168.0.5'
expect(subject).to include 'credentialType' => 'UsernamePasswordCredential'
expect(subject).to include 'isFromNewLogin' => 'true'
expect(subject).to include 'longTermAuthenticationRequestTokenUsed' => 'false'
expect(subject).to include 'nameIdentifier' => '1044957'
expect(subject).to include 'samlAuthenticationStatementAuthMethod' => 'urn:oasis:names:tc:SAML:1.0:am:password'
expect(subject).to include 'serverIpAddress' => '192.168.0.45'
expect(subject).to include 'successfulAuthenticationHandlers' => 'Static Credentials'
expect(subject).to include 'user' => nil
expect(subject).to include 'userAgent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
end
end

context 'when merging multivalued attributes' do
let(:provider_options) do
double('provider_options',
disable_ssl_verification?: false,
merge_multivalued_attributes: true,
ca_path: '/etc/ssl/certsZOMG'
)
end

it 'parses multivalued user info from the response' do
expect(subject).to include 'authenticationDate' => '2023-11-17T21:56:19.066445Z'
expect(subject).to include 'authenticationMethod' => 'Static Credentials'
expect(subject).to include 'clientIpAddress' => '192.168.0.5'
expect(subject).to include 'credentialType' => 'UsernamePasswordCredential'
expect(subject).to include 'isFromNewLogin' => 'true'
expect(subject).to include 'longTermAuthenticationRequestTokenUsed' => 'false'
expect(subject).to include 'nameIdentifier' => '1044957'
expect(subject).to include 'samlAuthenticationStatementAuthMethod' => 'urn:oasis:names:tc:SAML:1.0:am:password'
expect(subject).to include 'serverIpAddress' => '192.168.0.45'
expect(subject).to include 'successfulAuthenticationHandlers' => 'Static Credentials'
expect(subject).to include 'user' => nil
expect(subject).to include 'userAgent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
end
end
end
end

end

0 comments on commit be56dcb

Please sign in to comment.