ruby-saml-idp 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -0
- data/MIT-LICENSE +20 -0
- data/README.md +83 -0
- data/app/controllers/saml_idp/idp_controller.rb +40 -0
- data/app/views/saml_idp/idp/new.html.erb +21 -0
- data/app/views/saml_idp/idp/saml_post.html.erb +10 -0
- data/lib/ruby-saml-idp.rb +3 -0
- data/lib/saml-idp/controller.rb +68 -0
- data/lib/saml-idp/default.rb +29 -0
- data/lib/saml-idp/version.rb +3 -0
- data/ruby-saml-idp.gemspec +31 -0
- data/spec/controller_spec.rb +49 -0
- data/spec/spec_helper.rb +14 -0
- metadata +132 -0
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Lawrence Pit (http://lawrencepit.com)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# Ruby SAML IdP [![Build Status](https://secure.travis-ci.org/lawrencepit/ruby-saml-idp.png)](http://travis-ci.org/lawrencepit/ruby-saml-idp?branch=master) [![Dependency Status](https://gemnasium.com/lawrencepit/ruby-saml-idp.png)](https://gemnasium.com/lawrencepit/ruby-saml-idp)
|
2
|
+
|
3
|
+
The Ruby SAML IdP library is for implementing the server side of SAML authentication. It allows your application to act as an IdP (Identity Provider) using the [SAML v2.0](http://en.wikipedia.org/wiki/Security_Assertion_Markup_Language) protocol. It provides a means for managing authentication requests and confirmation responses for SPs (Service Providers).
|
4
|
+
|
5
|
+
Setting up a "real" IdP is such an undertaking I didn't care for such an achievement. I wanted something very simple that just works without having to install extra components. In it's current form it's very basic. This is because currently I use it for manual and end-to-end testing purposes only. It is reversed engineered from real-world SAML Responses send by ADFS systems.
|
6
|
+
|
7
|
+
|
8
|
+
Installation and Usage
|
9
|
+
----------------------
|
10
|
+
|
11
|
+
Add this to your Gemfile:
|
12
|
+
|
13
|
+
gem 'ruby-saml-idp'
|
14
|
+
|
15
|
+
### Not using rails?
|
16
|
+
|
17
|
+
Include `SamlIdp::Controller` and see the examples that use rails. It should be straightforward for you. Basically you call `decode_SAMLRequest(params[:SAMLRequest])` and then use the value `saml_acs_url` to determine the source for which you need to authenticate a user. Once a user has successfully authenticated on your system send the Service Provider a SAMLReponse by posting to `saml_acs_url` the parameter `SAMLResponse` with the return value from a call to `create_SAMLResponse(user_email, audience_uri, issuer_uri)`
|
18
|
+
|
19
|
+
### Using rails?
|
20
|
+
|
21
|
+
Add to your `routes.rb` file, for example:
|
22
|
+
|
23
|
+
``` ruby
|
24
|
+
get '/saml/auth' => 'saml_idp#new'
|
25
|
+
post '/saml/auth' => 'saml_idp#create'
|
26
|
+
```
|
27
|
+
|
28
|
+
Create a controller that looks like this, customize to your own situation:
|
29
|
+
|
30
|
+
``` ruby
|
31
|
+
class SamlIdpController < SamlIdp::IdpController
|
32
|
+
before_filter :find_account
|
33
|
+
# layout 'saml_idp'
|
34
|
+
|
35
|
+
def idp_authenticate(email, password)
|
36
|
+
user = @account.users.where(:email => params[:email]).first
|
37
|
+
user && user.valid_password?(params[:password]) ? user : nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def idp_make_saml_response(user)
|
41
|
+
create_SAMLResponse(user.email, "https://example.com")
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def find_account
|
47
|
+
@subdomain = saml_acs_url[/http:\/\/(.+?)\.example.com/, 1]
|
48
|
+
@account = Account.find_by_subdomain(@subdomain)
|
49
|
+
render :status => :forbidden unless @account.saml_enabled?
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
|
56
|
+
Keys and Secrets
|
57
|
+
----------------
|
58
|
+
|
59
|
+
To generate the SAML Response it uses a default X.509 certificate and secret key... which isn't so secret. You can find them in `SamlIdp::Default`. The X.509 certificate is valid until year 2032. Obviously you shouldn't use these if you intend to use this in production environments. In that case, within the controller set the properties `x509_certificate` and `secret_key` using a `prepend_before_filter` callback.
|
60
|
+
|
61
|
+
The fingerprint to use, if you use the default X.509 certificate of this gem, is:
|
62
|
+
|
63
|
+
```
|
64
|
+
9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D
|
65
|
+
```
|
66
|
+
|
67
|
+
|
68
|
+
Service Providers
|
69
|
+
-----------------
|
70
|
+
|
71
|
+
To act as a Service Provider which generates SAML Requests use the excellent [ruby-saml](https://github.com/onelogin/ruby-saml) gem.
|
72
|
+
|
73
|
+
|
74
|
+
Author
|
75
|
+
----------
|
76
|
+
|
77
|
+
Lawrence Pit, lawrence.pit@gmail.com, [lawrencepit.com](http://lawrencepit.com), [@lawrencepit](http://twitter.com/lawrencepit)
|
78
|
+
|
79
|
+
|
80
|
+
Copyright
|
81
|
+
-----------
|
82
|
+
|
83
|
+
Copyright (c) 2012 Lawrence Pit. See MIT-LICENSE for details.
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module SamlIdp
|
2
|
+
class IdpController < ActionController::Base
|
3
|
+
include SamlIdp::Controller
|
4
|
+
|
5
|
+
unloadable
|
6
|
+
|
7
|
+
protect_from_forgery
|
8
|
+
|
9
|
+
before_filter :validate_saml_request
|
10
|
+
|
11
|
+
def new
|
12
|
+
render :template => "saml_idp/idp/new"
|
13
|
+
end
|
14
|
+
|
15
|
+
def create
|
16
|
+
unless params[:email].blank? && params[:password].blank?
|
17
|
+
person = idp_authenticate(params[:email], params[:password])
|
18
|
+
if person.nil?
|
19
|
+
@saml_idp_fail_msg = "Incorrect email or password."
|
20
|
+
else
|
21
|
+
@saml_response = idp_make_saml_response(person)
|
22
|
+
render :template => "saml_idp/idp/saml_post", :layout => false
|
23
|
+
return
|
24
|
+
end
|
25
|
+
end
|
26
|
+
render :template => "saml_idp/idp/new"
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def idp_authenticate(email, password)
|
32
|
+
raise "Not implemented"
|
33
|
+
end
|
34
|
+
|
35
|
+
def idp_make_saml_response(person)
|
36
|
+
raise "Not implemented"
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<% if @saml_idp_fail_msg %>
|
2
|
+
<div id="saml_idp_fail_msg" class="flash error"><%= @saml_idp_fail_msg %></div>
|
3
|
+
<% end %>
|
4
|
+
|
5
|
+
<%= form_tag do %>
|
6
|
+
<%= hidden_field_tag("SAMLRequest", params[:SAMLRequest]) %>
|
7
|
+
|
8
|
+
<p>
|
9
|
+
<%= label_tag :email %>
|
10
|
+
<%= email_field_tag :email, params[:email], :autocapitalize => "off", :autocorrect => "off", :autofocus => "autofocus", :spellcheck => "false", :size => 30, :class => "email_pwd txt" %>
|
11
|
+
</p>
|
12
|
+
|
13
|
+
<p>
|
14
|
+
<%= label_tag :password %>
|
15
|
+
<%= password_field_tag :password, params[:password], :autocapitalize => "off", :autocorrect => "off", :spellcheck => "false", :size => 30, :class => "email_pwd txt" %>
|
16
|
+
</p>
|
17
|
+
|
18
|
+
<p>
|
19
|
+
<%= submit_tag "Sign in", :class => "button big blueish" %>
|
20
|
+
</p>
|
21
|
+
<% end %>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
6
|
+
</head>
|
7
|
+
<body onload="document.forms[0].submit();">
|
8
|
+
<%= form_tag(@saml_acs_url) { hidden_field_tag("SAMLResponse", @saml_response) } %>
|
9
|
+
</body>
|
10
|
+
</html>
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'base64'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module SamlIdp
|
6
|
+
module Controller
|
7
|
+
|
8
|
+
attr_accessor :x509_certificate, :secret_key
|
9
|
+
|
10
|
+
def saml_acs_url
|
11
|
+
@saml_acs_url
|
12
|
+
end
|
13
|
+
|
14
|
+
def x509_certificate
|
15
|
+
return @x509_certificate if defined?(@x509_certificate)
|
16
|
+
@x509_certificate = SamlIdp::Default::X509_CERTIFICATE
|
17
|
+
end
|
18
|
+
|
19
|
+
def secret_key
|
20
|
+
return @secret_key if defined?(@secret_key)
|
21
|
+
@secret_key = SamlIdp::Default::SECRET_KEY
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def validate_saml_request(saml_request = params[:SAMLRequest])
|
27
|
+
decode_SAMLRequest(saml_request)
|
28
|
+
end
|
29
|
+
|
30
|
+
def decode_SAMLRequest(saml_request)
|
31
|
+
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
32
|
+
text = zstream.inflate(Base64.decode64(saml_request))
|
33
|
+
zstream.finish
|
34
|
+
zstream.close
|
35
|
+
@saml_request_id = text[/ID='(.+?)'/, 1]
|
36
|
+
@saml_acs_url = text[/AssertionConsumerServiceURL='(.+?)'/, 1]
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_SAMLResponse(nameID, audience_uri, issuer_uri = "#{request.scheme}://#{request.host_with_port}#{request.fullpath}")
|
40
|
+
now = Time.now.utc
|
41
|
+
response_id, reference_id = UUID.generate, UUID.generate
|
42
|
+
|
43
|
+
assertion = %[<Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="_#{reference_id}" IssueInstant="#{now.iso8601}" Version="2.0"><Issuer>#{issuer_uri}</Issuer><Subject><NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">#{nameID}</NameID><SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><SubjectConfirmationData InResponseTo="#{@saml_request_id}" NotOnOrAfter="#{(now+3*60).iso8601}" Recipient="#{@saml_acs_url}"></SubjectConfirmationData></SubjectConfirmation></Subject><Conditions NotBefore="#{(now-5).iso8601}" NotOnOrAfter="#{(now+60*60).iso8601}"><AudienceRestriction><Audience>#{audience_uri}</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"><AttributeValue>#{nameID}</AttributeValue></Attribute></AttributeStatement><AuthnStatement AuthnInstant="#{now.iso8601}" SessionIndex="_#{reference_id}"><AuthnContext><AuthnContextClassRef>urn:federation:authentication:windows</AuthnContextClassRef></AuthnContext></AuthnStatement></Assertion>]
|
44
|
+
|
45
|
+
digest_value = Base64.encode64(OpenSSL::Digest::SHA1.digest(assertion)).chomp
|
46
|
+
|
47
|
+
signed_info = %[<ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></ds:SignatureMethod><ds:Reference URI="#_#{reference_id}"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod><ds:DigestValue>#{digest_value}</ds:DigestValue></ds:Reference></ds:SignedInfo>]
|
48
|
+
|
49
|
+
signature_value = sign(signed_info)
|
50
|
+
|
51
|
+
signature = %[<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">#{signed_info}<ds:SignatureValue>#{signature_value}</ds:SignatureValue><KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data><ds:X509Certificate>#{self.x509_certificate}</ds:X509Certificate></ds:X509Data></KeyInfo></ds:Signature>]
|
52
|
+
|
53
|
+
assertion_and_signature = assertion.sub(/Issuer\>\<Subject/, "Issuer>#{signature}<Subject")
|
54
|
+
|
55
|
+
xml = %[<samlp:Response ID="_#{response_id}" Version="2.0" IssueInstant="#{now.iso8601}" Destination="#{@saml_acs_url}" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified" InResponseTo="#{@saml_request_id}" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">#{issuer_uri}</Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /></samlp:Status>#{assertion_and_signature}</samlp:Response>]
|
56
|
+
|
57
|
+
Base64.encode64(xml)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def sign(data)
|
63
|
+
key = OpenSSL::PKey::RSA.new(self.secret_key)
|
64
|
+
Base64.encode64(key.sign(OpenSSL::Digest::SHA1.new, data))
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module SamlIdp
|
2
|
+
module Default
|
3
|
+
|
4
|
+
NAME_ID_FORMAT = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
5
|
+
|
6
|
+
X509_CERTIFICATE = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURxekNDQXhTZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBRENCaGpFTE1Ba0dBMVVFQmhNQ1FWVXgKRERBS0JnTlZCQWdUQTA1VFZ6RVBNQTBHQTFVRUJ4TUdVM2xrYm1WNU1Rd3dDZ1lEVlFRS0RBTlFTVlF4Q1RBSApCZ05WQkFzTUFERVlNQllHQTFVRUF3d1BiR0YzY21WdVkyVndhWFF1WTI5dE1TVXdJd1lKS29aSWh2Y05BUWtCCkRCWnNZWGR5Wlc1alpTNXdhWFJBWjIxaGFXd3VZMjl0TUI0WERURXlNRFF5T0RBeU1qSXlPRm9YRFRNeU1EUXkKTXpBeU1qSXlPRm93Z1lZeEN6QUpCZ05WQkFZVEFrRlZNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVApCbE41Wkc1bGVURU1NQW9HQTFVRUNnd0RVRWxVTVFrd0J3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psCmJtTmxjR2wwTG1OdmJURWxNQ01HQ1NxR1NJYjNEUUVKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnYKYlRDQm56QU5CZ2txaGtpRzl3MEJBUUVGQUFPQmpRQXdnWWtDZ1lFQXVCeXdQTmxDMUZvcEdMWWZGOTZTb3RpSwo4Tmo2L25XMDg0TzRvbVJNaWZ6eTd4OTU1UkxFeTY3M3EyYWlKTkIzTHZFNlh2a3Q5Y0d0eHROb09YdzFnMlV2CkhLcGxkUWJyNmJPRWpMTmVETlc3ajBvYitKclJ2QVVPSzlDUmdkeXc1TUM2bHdxVlFRNUMxRG5hVC8yZlNCRmoKYXNCRlRSMjRkRXBmVHk4SGZLRUNBd0VBQWFPQ0FTVXdnZ0VoTUFrR0ExVWRFd1FDTUFBd0N3WURWUjBQQkFRRApBZ1VnTUIwR0ExVWREZ1FXQkJRTkJHbW10M3l0S3BjSmFCYVlOYm55VTJ4a2F6QVRCZ05WSFNVRUREQUtCZ2dyCkJnRUZCUWNEQVRBZEJnbGdoa2dCaHZoQ0FRMEVFQllPVkdWemRDQllOVEE1SUdObGNuUXdnYk1HQTFVZEl3U0IKcXpDQnFJQVVEUVJwcHJkOHJTcVhDV2dXbURXNThsTnNaR3VoZ1l5a2dZa3dnWVl4Q3pBSkJnTlZCQVlUQWtGVgpNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVEJsTjVaRzVsZVRFTU1Bb0dBMVVFQ2d3RFVFbFVNUWt3CkJ3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psYm1ObGNHbDBMbU52YlRFbE1DTUdDU3FHU0liM0RRRUoKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnZiWUlCQVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9CZ1FBRQpjVlVQQlg3dVptenFaSmZ5K3RVUE9UNUltTlFqOFZFMmxlcmhuRmpuR1BIbUhJcWhwemdud0hRdWpKZnMvYTMwCjlXbTVxd2NDYUMxZU81Y1dqY0cweDNPamRsbHNnWURhdGw1R0F1bXRCeDhKM05oV1JxTlVnaXRDSWtRbHhISXcKVWZnUWFDdXNoWWdEREw1WWJJUWErK2VnQ2dwSVorVDBEajVvUmV3Ly9BPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo="
|
7
|
+
|
8
|
+
FINGERPRINT = "9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D"
|
9
|
+
|
10
|
+
SECRET_KEY = <<EOS
|
11
|
+
-----BEGIN RSA PRIVATE KEY-----
|
12
|
+
MIICXAIBAAKBgQC4HLA82ULUWikYth8X3pKi2Irw2Pr+dbTzg7iiZEyJ/PLvH3nl
|
13
|
+
EsTLrverZqIk0Hcu8Tpe+S31wa3G02g5fDWDZS8cqmV1Buvps4SMs14M1buPShv4
|
14
|
+
mtG8BQ4r0JGB3LDkwLqXCpVBDkLUOdpP/Z9IEWNqwEVNHbh0Sl9PLwd8oQIDAQAB
|
15
|
+
AoGAQmUGIUtwUEgbXe//kooPc26H3IdDLJSiJtcvtFBbUb/Ik/dT7AoysgltA4DF
|
16
|
+
pGURNfqERE+0BVZNJtCCW4ixew4uEhk1XowYXHCzjkzyYoFuT9v5SP4cu4q3t1kK
|
17
|
+
51JF237F0eCY3qC3k96CzPGG67bwOu9EeXAu4ka/plLdsAECQQDkg0uhR/vsJffx
|
18
|
+
tiWxcDRNFoZpCpzpdWfQBnHBzj9ZC0xrdVilxBgBpupCljO2Scy4MeiY4S1Mleel
|
19
|
+
CWRqh7RBAkEAzkIjUnllEkr5sjVb7pNy+e/eakuDxvZck0Z8X3USUki/Nm3E/GPP
|
20
|
+
c+CwmXR4QlpMpJr3/Prf1j59l/LAK9AwYQJBAL9eRSQYCJ3HXlGKXR6v/NziFEY7
|
21
|
+
oRTSQdIw02ueseZ8U89aQpbwFbqsclq5Ny1duJg5E7WUPj94+rl3mCSu6QECQBVh
|
22
|
+
0duY7htpXl1VHsSq0H6MmVgXn/+eRpaV9grHTjDtjbUMyCEKD9WJc4VVB6qJRezC
|
23
|
+
i/bT4ySIsehwp+9i08ECQEH03lCpHpbwiWH4sD25l/z3g2jCbIZ+RTV6yHIz7Coh
|
24
|
+
gAbBqA04wh64JhhfG69oTBwqwj3imlWF8+jDzV9RNNw=
|
25
|
+
-----END RSA PRIVATE KEY-----
|
26
|
+
EOS
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "saml-idp/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = %q{ruby-saml-idp}
|
7
|
+
s.version = SamlIdp::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Lawrence Pit"]
|
10
|
+
s.email = %q{lawrence.pit@gmail.com}
|
11
|
+
s.homepage = %q{http://github.com/lawrencepit/ruby-saml-idp}
|
12
|
+
s.summary = %q{SAML IdP in ruby}
|
13
|
+
s.description = %q{SAML Identity Provider library in ruby}
|
14
|
+
s.date = Time.now.utc.strftime("%Y-%m-%d")
|
15
|
+
s.files = Dir.glob("app/**/*") + Dir.glob("lib/**/*") + [
|
16
|
+
"MIT-LICENSE",
|
17
|
+
"README.md",
|
18
|
+
"Gemfile",
|
19
|
+
"ruby-saml-idp.gemspec"
|
20
|
+
]
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
25
|
+
s.add_dependency('uuid')
|
26
|
+
s.add_development_dependency "rake"
|
27
|
+
# s.add_development_dependency "rcov"
|
28
|
+
s.add_development_dependency "rspec"
|
29
|
+
s.add_development_dependency "ruby-saml"
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe SamlIdp::Controller do
|
5
|
+
include SamlIdp::Controller
|
6
|
+
|
7
|
+
def params
|
8
|
+
@params ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should find the SAML ACS URL" do
|
12
|
+
requested_saml_acs_url = "https://foo.example.com/saml/consume"
|
13
|
+
auth_request = Onelogin::Saml::Authrequest.new
|
14
|
+
auth_url = auth_request.create(saml_settings(requested_saml_acs_url))
|
15
|
+
params[:SAMLRequest] = CGI.unescape(auth_url.split("=").last)
|
16
|
+
|
17
|
+
validate_saml_request
|
18
|
+
saml_acs_url.should == requested_saml_acs_url
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should create a SAML Response" do
|
22
|
+
requested_saml_acs_url = "https://foo.example.com/saml/consume"
|
23
|
+
saml_config = saml_settings(requested_saml_acs_url)
|
24
|
+
auth_request = Onelogin::Saml::Authrequest.new
|
25
|
+
auth_url = auth_request.create(saml_config)
|
26
|
+
params[:SAMLRequest] = CGI.unescape(auth_url.split("=").last)
|
27
|
+
validate_saml_request
|
28
|
+
saml_response = create_SAMLResponse("foo@example.com", "https://idp.com/saml/idp", "https://idp.com")
|
29
|
+
|
30
|
+
response = Onelogin::Saml::Response.new(saml_response)
|
31
|
+
response.name_id.should == "foo@example.com"
|
32
|
+
response.issuer.should == "https://idp.com"
|
33
|
+
response.settings = saml_config
|
34
|
+
response.is_valid?.should be_true
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def saml_settings(saml_acs_url)
|
40
|
+
settings = Onelogin::Saml::Settings.new
|
41
|
+
settings.assertion_consumer_service_url = saml_acs_url
|
42
|
+
settings.issuer = "http://example.com"
|
43
|
+
settings.idp_sso_target_url = "http://idp.com/saml/idp"
|
44
|
+
settings.idp_cert_fingerprint = SamlIdp::Default::FINGERPRINT
|
45
|
+
settings.name_identifier_format = SamlIdp::Default::NAME_ID_FORMAT
|
46
|
+
settings
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
3
|
+
$LOAD_PATH.unshift File.dirname(__FILE__)
|
4
|
+
|
5
|
+
STDERR.puts("Running Specs under Ruby Version #{RUBY_VERSION}")
|
6
|
+
|
7
|
+
require 'rspec'
|
8
|
+
require 'ruby-saml'
|
9
|
+
require 'ruby-saml-idp'
|
10
|
+
|
11
|
+
RSpec.configure do |c|
|
12
|
+
c.mock_with :rspec
|
13
|
+
end
|
14
|
+
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-saml-idp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 9
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: "0.1"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Lawrence Pit
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2012-04-28 00:00:00 Z
|
18
|
+
dependencies:
|
19
|
+
- !ruby/object:Gem::Dependency
|
20
|
+
name: uuid
|
21
|
+
prerelease: false
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
hash: 3
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rake
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
hash: 3
|
42
|
+
segments:
|
43
|
+
- 0
|
44
|
+
version: "0"
|
45
|
+
type: :development
|
46
|
+
version_requirements: *id002
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
prerelease: false
|
50
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
hash: 3
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
version: "0"
|
59
|
+
type: :development
|
60
|
+
version_requirements: *id003
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: ruby-saml
|
63
|
+
prerelease: false
|
64
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
hash: 3
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
version: "0"
|
73
|
+
type: :development
|
74
|
+
version_requirements: *id004
|
75
|
+
description: SAML Identity Provider library in ruby
|
76
|
+
email: lawrence.pit@gmail.com
|
77
|
+
executables: []
|
78
|
+
|
79
|
+
extensions: []
|
80
|
+
|
81
|
+
extra_rdoc_files: []
|
82
|
+
|
83
|
+
files:
|
84
|
+
- app/controllers/saml_idp/idp_controller.rb
|
85
|
+
- app/views/saml_idp/idp/new.html.erb
|
86
|
+
- app/views/saml_idp/idp/saml_post.html.erb
|
87
|
+
- lib/ruby-saml-idp.rb
|
88
|
+
- lib/saml-idp/controller.rb
|
89
|
+
- lib/saml-idp/default.rb
|
90
|
+
- lib/saml-idp/version.rb
|
91
|
+
- MIT-LICENSE
|
92
|
+
- README.md
|
93
|
+
- Gemfile
|
94
|
+
- ruby-saml-idp.gemspec
|
95
|
+
- spec/controller_spec.rb
|
96
|
+
- spec/spec_helper.rb
|
97
|
+
homepage: http://github.com/lawrencepit/ruby-saml-idp
|
98
|
+
licenses: []
|
99
|
+
|
100
|
+
post_install_message:
|
101
|
+
rdoc_options:
|
102
|
+
- --charset=UTF-8
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
hash: 3
|
111
|
+
segments:
|
112
|
+
- 0
|
113
|
+
version: "0"
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
hash: 3
|
120
|
+
segments:
|
121
|
+
- 0
|
122
|
+
version: "0"
|
123
|
+
requirements: []
|
124
|
+
|
125
|
+
rubyforge_project:
|
126
|
+
rubygems_version: 1.8.24
|
127
|
+
signing_key:
|
128
|
+
specification_version: 3
|
129
|
+
summary: SAML IdP in ruby
|
130
|
+
test_files:
|
131
|
+
- spec/controller_spec.rb
|
132
|
+
- spec/spec_helper.rb
|