rack-saml 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ #group :example do
6
+ # gem 'sinatra'
7
+ #end
data/README.md ADDED
@@ -0,0 +1,171 @@
1
+ # SAML (Shibboleth SP) middleware for Rack
2
+
3
+ This project is deeply inspired by rack-shibboleth and ruby-saml. It is recommended to use the defact SAML implementation such as OpenSAML from the security or the functional aspect. However, there are also requirements to use SAML for light weight applications implemented by Ruby. rack-shibboleth may be a candidate to support such kind of objective. However it lacks the configurability to fit OmniAuth and the upgrade path to secure and stable middleware as OpenSAML. So thus I just implemented a prototype to support SAML (Shibboleth SP) for Rack middleware.
4
+
5
+ OmniAuth Shibboleth Strategy
6
+ https://github.com/toyokazu/omniauth-shibboleth
7
+
8
+ ## Limitations
9
+
10
+ ### Signing AuthnRequest
11
+
12
+ Current implementation supports only Onelogin SAML assertion handler. It does not support to sign AuthnRequest.
13
+
14
+ ### Encrypted Assertion
15
+
16
+ Current implementation does not support assertion encryption. So thus, assertion encription function should be disabled for rack-saml SPs.
17
+
18
+ ### SP Session
19
+
20
+ Current implementation does not support keeping session at SP side.
21
+
22
+ ## Getting Started
23
+
24
+ ### Installation
25
+
26
+ % gem install rack-saml
27
+
28
+ ### Setup Gemfile
29
+
30
+ % cd rails-app
31
+ % vi Gemfile
32
+ gem 'rack-saml'
33
+
34
+ ### Setup Rack::Saml middleware
35
+
36
+ #### For Rack applicaitons
37
+
38
+ In the following example, config.ru is used to add Rack::Saml middleware into a Rails application.
39
+
40
+ % vi config.ru
41
+ use Rack::Saml, {:saml_config => "#{Rails.root}/config/saml.yml",
42
+ :metadata => "#{Rails.root}/config/metadata.yml",
43
+ :attribute_map => "#{Rails.root}/config/attribute-map.yml",
44
+ :shib_app_id => "default"}
45
+
46
+ #### For Ralis applications
47
+
48
+ In the following example, config/application.rb is used to Rack::Saml middleware into a Rails application.
49
+
50
+ % vi config/application.rb
51
+ module TestRackSaml
52
+ class Application < Rails::Application
53
+ config.middleware.use Rack::Saml, {:saml_config => "#{Rails.root}/config/saml.yml",
54
+ :metadata => "#{Rails.root}/config/metadata.yml",
55
+ :attribute_map => "#{Rails.root}/config/attribute-map.yml"
56
+ :shib_app_id => "default"}
57
+ ...
58
+
59
+ If you just want to test Rack::Saml, you can ommit middleware options in the both example (config.ru or config/application.rb).
60
+
61
+ use Rack::Saml
62
+
63
+ However, you can not omit :shib_app_id option if you want to use this middleware with OmniAuth::Shibboleth Strategy.
64
+
65
+ Rack::Saml uses default configurations located in the rack-saml gem path.
66
+
67
+ $GEM_HOME/rack-saml-x.x.x/config/xxx.yml
68
+
69
+ #### Middleware options
70
+
71
+ * *:saml_config* path to saml.yml file
72
+ * *:metadata* path to metadata.yml file
73
+ * *:attribute_map* path to attribute-map.yml file
74
+ * *:shib_app_id* If you want to use the middleware as Shibboleth SP, you should specify an application ID. In the Shibboleth SP default configuration, 'default' is used as the application ID.
75
+
76
+
77
+ #### Configuration files
78
+
79
+ You can find default configuration files at
80
+
81
+ $GEM_HOME/rack-saml-x.x.x/config/
82
+
83
+ ##### saml.yml
84
+
85
+ Configuration to set SAML parameters.
86
+
87
+ * *protected_path* string or regular expression of the path name where SAML protects, e.g. /auth/shibboleth/callback or '^\/secure\/[^\s]*'
88
+ * *protected_path_regexp* use regular expression to match protected_path or not e.g. true / false
89
+ * *metadata_path* the path name where SP's metadata is generated
90
+ * *assertion_handler* 'onelogin' / 'opensaml' (not implemented yet)
91
+ * *saml_idp* idp_entity_id
92
+ * *saml_sp* sp_entity_id (self id)
93
+ * *sp_cert* path to the SAML SP's certificate file, e.g. cert.pem (signing AuthnRequest is not supported yet)
94
+ * *sp_key* path to the SAML SP's key file, e.g. key.pem (signing AuthnRequest is not supported yet)
95
+
96
+ ##### metadata.yml
97
+
98
+ To connect to an IdP, you must describe IdP's specification. In rack-saml, it should be written in metadata.yml. metadata.yml file include the following lists.
99
+
100
+ * *idp_lists* list of IdP metadata
101
+ * *sp_lists* list of SP metadata
102
+
103
+ idp_lists and sp_lists are hashes which have entity ids as key values.
104
+
105
+ parameters of the idp_lists:
106
+
107
+ * *certificate* base64 encoded certificate of IdP
108
+ * *saml2_http_redirect* Location attribute of the IdP's assertion handler uri with HTTP Redirect Binding
109
+
110
+ parameters of the sp_lists (currently not used):
111
+
112
+ * *certificate* base64 encoded certificate of SP
113
+ * *saml2_http_post* Location attribute of the SP's assertion consumer uri with HTTP POST Binding
114
+
115
+ These parameters are automatically extracted from SAML metadata. You can use conv_metadata.rb command for extraction.
116
+
117
+ % $GEM_HOME/rack-saml-x.x.x/bin/conv_metadata.rb metadata.xml > metadata.yml
118
+
119
+ ##### attribute-map.yml
120
+
121
+ attribute-map.yml can extract attributes from SAML Response and put attributes on request environment variables. It is useful to pass attributes into applications. The configuration file format is as follows:
122
+
123
+ "Attribute Name": "Environment Variable Name"
124
+ "urn:oid:0.9.2342.19200300.100.1.1": "uid"
125
+ ...
126
+
127
+ ### Setup IdP to accept rack-saml SP
128
+
129
+ #### SP Metadata generation
130
+
131
+ To connect a new SP to the existing IdP, you need to import SP's metadata into the IdP. rack-saml provides metadata generation function. It is generated at '/Shibboleth.sso/Metadata' by default.
132
+
133
+ #### IdP configuration examples not to encrypt assertion
134
+
135
+ Current rack-saml implementation does not support assertion encryption because Onelogin::Saml does not support signature and encryption of assertion. So thus, in the followings, we would like to show sample configurations to disable encryption in IdP assertion processing. These are not recommended for sensitive applications.
136
+
137
+ ##### Shibboleth IdP example
138
+
139
+ Add the following configuration after <rp:DefaultRelyingParty> in relying-party.xml. You should specify sp entity id at the 'id' and the 'provider' attributes.
140
+
141
+ % vi $IDP_HOME/conf/relying-party.xml
142
+ ...
143
+ <rp:RelyingParty id="http://example.com:3000/auth/shibboleth/callback" provider="http://example.com:3000/auth/shibboleth/callback" defaultSigningCredentialRef="IdPCredential">
144
+ <rp:ProfileConfiguration xsi:type="saml:SAML2SSOProfile" includeAttributeStatement="true" assertionLifetime="PT5M" assertionProxyCount="0" signResponses="never" signAssertions="always" encryptAssertions="never" encryptNameIds="never"/>
145
+ </rp:RelyingParty>
146
+
147
+ ## TODO
148
+
149
+ * ruby-opensaml (I hope someone implement it :)
150
+
151
+ ## License
152
+
153
+ Copyright (C) 2011 by Toyokazu Akiyama.
154
+
155
+ Permission is hereby granted, free of charge, to any person obtaining a copy
156
+ of this software and associated documentation files (the "Software"), to deal
157
+ in the Software without restriction, including without limitation the rights
158
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
159
+ copies of the Software, and to permit persons to whom the Software is
160
+ furnished to do so, subject to the following conditions:
161
+
162
+ The above copyright notice and this permission notice shall be included in
163
+ all copies or substantial portions of the Software.
164
+
165
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
166
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
167
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
168
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
169
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
170
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
171
+ THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :default => :spec
6
+ task :test => :spec
7
+
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rexml/document'
3
+ require 'uri'
4
+ require 'yaml'
5
+
6
+ DS = 'http://www.w3.org/2000/09/xmldsig#'
7
+
8
+ if ARGV.size < 1
9
+ puts "outputs yaml format metadata file"
10
+ puts "usage: conv_metadata.rb metadata_file"
11
+ exit(1)
12
+ end
13
+
14
+ file = File.new(ARGV[0])
15
+ doc = REXML::Document.new(file)
16
+
17
+ def get_list_type(elem)
18
+ if !elem.elements["IDPSSODescriptor"].nil?
19
+ return "idp_lists"
20
+ end
21
+ "sp_lists"
22
+ end
23
+
24
+ def create_entity_hash(elem, list_type)
25
+ case list_type
26
+ when "idp_lists"
27
+ idp_elem = elem.elements["IDPSSODescriptor"]
28
+ # the first certificate is used
29
+ certificate = "-----BEGIN CERTIFICATE-----#{REXML::XPath.first(idp_elem, './/ds:X509Certificate', 'ds' => DS).text.gsub(/\s*$/, "")}\n-----END CERTIFICATE-----"
30
+ saml2_http_redirect = nil
31
+ idp_elem.elements.each("SingleSignOnService") do |e|
32
+ if e.attributes["Binding"] == "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
33
+ saml2_http_redirect = e.attributes["Location"]
34
+ end
35
+ end
36
+ return {"certificate" => certificate,
37
+ "saml2_http_redirect" => saml2_http_redirect}
38
+ when "sp_lists"
39
+ sp_elem = elem.elements["SPSSODescriptor"]
40
+ # the first certificate is used
41
+ certificate = REXML::XPath.first(sp_elem, './/ds:X509Certificate', 'ds' => DS).text
42
+ saml2_http_post = nil
43
+ sp_elem.elements.each("AssertionConsumerService") do |e|
44
+ if e.attributes["Binding"] == "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
45
+ saml2_http_post = e.attributes["Location"]
46
+ end
47
+ end
48
+ return {"certificate" => certificate,
49
+ "saml2_http_post" => saml2_http_post}
50
+ end
51
+ end
52
+
53
+ def add_entities(entities, elem)
54
+ list_type = get_list_type(elem)
55
+ entity_id = elem.attributes["entityID"]
56
+ entities[list_type][entity_id] = create_entity_hash(elem, list_type)
57
+ end
58
+
59
+ entities = {"idp_lists" => {}, "sp_lists" => {}}
60
+ doc.elements.each("EntityDescriptor") do |elem|
61
+ add_entities(entities, elem)
62
+ end
63
+
64
+ doc.elements.each("EntitiesDescriptor/EntityDescriptor") do |elem|
65
+ add_entities(entities, elem)
66
+ end
67
+
68
+ puts entities.to_yaml
@@ -0,0 +1,8 @@
1
+ "urn:oid:1.3.6.1.4.1.5923.1.1.1.6": "eppn"
2
+ "urn:oid:1.3.6.1.4.1.5923.1.1.1.9": "affiliation"
3
+ "urn:oid:1.3.6.1.4.1.5923.1.1.1.1": "unscoped-affiliation"
4
+ "urn:oid:1.3.6.1.4.1.5923.1.1.1.7": "entitlement"
5
+ "urn:oid:0.9.2342.19200300.100.1.1": "uid"
6
+ "urn:oid:0.9.2342.19200300.100.1.3": "mail"
7
+ "urn:oid:2.16.840.1.113730.3.1.241": "displayName"
8
+ "urn:oid:1.3.6.1.4.1.5923.1.1.1.10": "persistent-id"
@@ -0,0 +1,15 @@
1
+ "urn:mace:dir:attribute-def:eduPersonPrincipalName": "eppn"
2
+ "urn:oid:1.3.6.1.4.1.5923.1.1.1.6": "eppn"
3
+ "urn:mace:dir:attribute-def:eduPersonScopedAffiliation": "affiliation"
4
+ "urn:oid:1.3.6.1.4.1.5923.1.1.1.9": "affiliation"
5
+ "urn:mace:dir:attribute-def:eduPersonAffiliation": "unscoped-affiliation"
6
+ "urn:oid:1.3.6.1.4.1.5923.1.1.1.1": "unscoped-affiliation"
7
+ "urn:mace:dir:attribute-def:eduPersonEntitlement": "entitlement"
8
+ "urn:oid:1.3.6.1.4.1.5923.1.1.1.7": "entitlement"
9
+ "urn:mace:dir:attribute-def:uid": "uid"
10
+ "urn:oid:0.9.2342.19200300.100.1.1": "uid"
11
+ "urn:mace:dir:attribute-def:mail": "mail"
12
+ "urn:oid:0.9.2342.19200300.100.1.3": "mail"
13
+ "urn:mace:dir:attribute-def:displayName": "displayName"
14
+ "urn:oid:2.16.840.1.113730.3.1.241": "displayName"
15
+ "urn:oid:1.3.6.1.4.1.5923.1.1.1.10": "persistent-id"
@@ -0,0 +1,32 @@
1
+ ---
2
+ idp_lists:
3
+ https://localhost/idp/shibboleth:
4
+ certificate: |-
5
+ -----BEGIN CERTIFICATE-----
6
+ MIIEKDCCAxCgAwIBAgIJALh5qhU6z0gMMA0GCSqGSIb3DQEBBQUAMIGKMQswCQYD
7
+ VQQGEwJKUDERMA8GA1UEBwwIQWNhZGVtZTIxIDAeBgNVBAoMF0t5b3RvIFNhbmd5
8
+ byBVbml2ZXJzaXR5MTQwMgYDVQQLDCtGYWN1bHR5IG9mIENvbXB1dGVyIFNjaWVu
9
+ Y2UgYW5kIEVuZ2luZWVyaW5nMRAwDgYDVQQDDAdUZXN0IENBMB4XDTExMDgxMDA3
10
+ MTY1OFoXDTEyMDgwOTA3MTY1OFowgZ4xCzAJBgNVBAYTAkpQMREwDwYDVQQHDAhB
11
+ Y2FkZW1lMjEgMB4GA1UECgwXS3lvdG8gU2FuZ3lvIFVuaXZlcnNpdHkxNDAyBgNV
12
+ BAsMK0ZhY3VsdHkgb2YgQ29tcHV0ZXIgU2NpZW5jZSBhbmQgRW5naW5lZXJpbmcx
13
+ JDAiBgNVBAMMG2lkcC50ZXN0LmNzZS5reW90by1zdS5hYy5qcDCCASIwDQYJKoZI
14
+ hvcNAQEBBQADggEPADCCAQoCggEBAN1/g5HoajtIFLrFGEt6z6vyWS7CNhz/nOw9
15
+ 7Ei0R7TYg/iwz3CuJdowlz7VnVTi/Oi2kz+YxuLw3RDwl5r16AKRCePyc0F63xSv
16
+ 5rc6kJkXBqGZlKtSn5OeSa+w18c4MOtu4zSZP7wHhS5bMABUL8UBX1P021bPd7Ad
17
+ 6gMt9mCF+0giIyTWMP8PH8EoDTMPq+ko1QosxSxPSaERB2+OEmwBa4NnKyBmB9lU
18
+ NrWN5tB+BJ8qD9C+5EU+miWFzY59GC1JPc7TmQ28frobmgc22pUc6d/0+uBELEbx
19
+ v7G942PYDCdF1V0chRyGAP4/7IAH77B5t6wIqhLBffawb993mi8CAwEAAaN7MHkw
20
+ CQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2Vy
21
+ dGlmaWNhdGUwHQYDVR0OBBYEFDpB4W//bAwVauJd/pvUOfZl72eqMB8GA1UdIwQY
22
+ MBaAFINIlekZI9dBQNn6x8vCKxS3h6CSMA0GCSqGSIb3DQEBBQUAA4IBAQBZwuZp
23
+ TyczweI8L68yzkq//5ORzkoJtW29aftSfWrXIO4/ckydyqYNHW1H62J4QtMxljHG
24
+ ZK+GAALGKIAYQTD805Ha4tezY/bpXB1HTu+E2e2jL6AmYEP62WcFdCmnPS7DSQ78
25
+ LDLDDmrPBRfNTVxgjEq1GRS1JfQJb8JrNipG+YqCinNVKuEx4wsc7bIRbY0YZrVp
26
+ +sRk6BB3HrOY1p+F/83in45wyNxGfgZpmdAvk9yB8ubzBhDaaNSqeU33iOGsqSBH
27
+ jD2RyhOELbPlOMEHn+q2vSZdlo4hqRpamhahSBsuiQFbcwpTkWJ3SJyjYYn845Xw
28
+ Uq9SgXh2ssVUFkni
29
+ -----END CERTIFICATE-----
30
+ saml2_http_redirect: https://localhost/idp/profile/SAML2/Redirect/SSO
31
+ sp_lists: {}
32
+
data/config/saml.yml ADDED
@@ -0,0 +1,6 @@
1
+ protected_path: /auth/shibboleth/callback
2
+ # protected_path_regexp: true
3
+ metadata_path: /Shibboleth.sso/Metadata
4
+ assertion_handler: onelogin
5
+ saml_idp: https://localhost/idp/shibboleth
6
+ saml_sp: http://localhost:3000/auth/shibboleth/callback
data/lib/rack-saml.rb ADDED
@@ -0,0 +1 @@
1
+ require 'rack/saml'
@@ -0,0 +1,6 @@
1
+ require 'rack'
2
+ module Rack
3
+ module Saml
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
data/lib/rack/saml.rb ADDED
@@ -0,0 +1,108 @@
1
+ require 'rack'
2
+ require 'yaml'
3
+
4
+ module Rack
5
+ # Rack::Saml
6
+ #
7
+ # As the Shibboleth SP, Rack::Saml::Base adopts :protected_path
8
+ # as an :assertion_consumer_path. It is easy to configure and
9
+ # support omniauth-shibboleth.
10
+ # To establish single path behavior, it currently supports only
11
+ # HTTP Redirect Binding from SP to Idp
12
+ # HTTP POST Binding from IdP to SP
13
+ class Saml
14
+ autoload "RequestHandler", 'rack/saml/request_handler'
15
+ autoload "MetadataHandler", 'rack/saml/metadata_handler'
16
+ autoload "ResponseHandler", 'rack/saml/response_handler'
17
+
18
+ class SamlAssertionError < StandardError
19
+ end
20
+
21
+ def default_config_path(config_file)
22
+ ::File.expand_path("../../../config/#{config_file}", __FILE__)
23
+ end
24
+
25
+ def default_saml_config
26
+ default_config_path('saml.yml')
27
+ end
28
+
29
+ def default_metadata
30
+ default_config_path('metadata.yml')
31
+ end
32
+
33
+ def default_attribute_map
34
+ default_config_path('attribute-map.yml')
35
+ end
36
+
37
+ def initialize app, opts = {}
38
+ @app = app
39
+ @opts = opts
40
+
41
+ if @opts[:saml_config].nil? || !::File.exists?(@opts[:saml_config])
42
+ @opts[:saml_config] = default_saml_config
43
+ end
44
+ @saml_config = YAML.load_file(@opts[:saml_config])
45
+ if @saml_config['assertion_handler'].nil?
46
+ raise ArgumentError, "'assertion_handler' parameter should be specified in the :saml_config file"
47
+ end
48
+ if @opts[:metadata].nil? || !::File.exists?(@opts[:metadata])
49
+ @opts[:metadata] = default_metadata
50
+ end
51
+ @metadata = YAML.load_file(@opts[:metadata])
52
+ if @opts[:attribute_map].nil? || !::File.exists?(@opts[:attribute_map])
53
+ @opts[:attribute_map] = default_attribute_map
54
+ end
55
+ @attribute_map = YAML.load_file(@opts[:attribute_map])
56
+ end
57
+
58
+ def call env
59
+ request = Rack::Request.new env
60
+ #return [
61
+ # 403,
62
+ # {
63
+ # 'Content-Type' => 'text/plain'
64
+ # },
65
+ # ["Forbidden." + request.inspect]
66
+ # ["Forbidden." + env.to_a.map {|i| "#{i[0]}: #{i[1]}"}.join("\n")]
67
+ #]
68
+ if request.request_method == 'GET'
69
+ if match_protected_path?(request) # generate AuthnRequest
70
+ handler = RequestHandler.new(request, @saml_config, @metadata['idp_lists'][@saml_config['saml_idp']])
71
+ return Rack::Response.new.tap { |r|
72
+ r.redirect handler.authn_request.redirect_uri
73
+ }.finish
74
+ elsif match_metadata_path?(request) # generate Metadata
75
+ handler = MetadataHandler.new(request, @saml_config, @metadata['idp_lists'][@saml_config['saml_idp']])
76
+ return [
77
+ 200,
78
+ {
79
+ 'Content-Type' => 'application/samlmetadata+xml'
80
+ },
81
+ [handler.sp_metadata.generate]
82
+ ]
83
+ end
84
+ elsif request.request_method == 'POST' && match_protected_path?(request) # process Response
85
+ handler = ResponseHandler.new(request, @saml_config, @metadata['idp_lists'][@saml_config['saml_idp']])
86
+ if handler.response.is_valid?
87
+ handler.extract_attrs(env, @attribute_map, @opts)
88
+ else
89
+ raise SamlAssertionError, "Invalid SAML response."
90
+ end
91
+ end
92
+
93
+ @app.call env
94
+ end
95
+
96
+ def match_protected_path?(request)
97
+ if @saml_config['protected_path_regexp']
98
+ # to be fixed (Regexp)
99
+ return (request.path_info =~ Regexp.new(@saml_config['protected_path']))
100
+ end
101
+ request.path_info == @saml_config['protected_path']
102
+ end
103
+
104
+ def match_metadata_path?(request)
105
+ request.path_info == @saml_config['metadata_path']
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,16 @@
1
+ module Rack
2
+ class Saml
3
+ class AbstractMetadata
4
+ attr_reader :request, :saml_config, :metadata
5
+
6
+ def initialize(request, saml_config, metadata)
7
+ @request = request
8
+ @saml_config = saml_config
9
+ @metadata = metadata
10
+ end
11
+
12
+ def generate
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ module Rack
2
+ class Saml
3
+ require 'rack/saml/misc/onelogin_setting'
4
+
5
+ class OneloginMetadata < AbstractMetadata
6
+ include OneloginSetting
7
+
8
+ def initialize(request, saml_config, metadata)
9
+ super(request, saml_config, metadata)
10
+ @sp_metadata = Onelogin::Saml::Metadata.new
11
+ end
12
+
13
+ def generate
14
+ @sp_metadata.generate(saml_settings)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ module Rack
2
+ class Saml
3
+ class OpensamlMetadata < AbstractMetadata
4
+ # to be implemented
5
+ def initialize(request, saml_config, metadata)
6
+ end
7
+
8
+ def generate
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,19 @@
1
+ module Rack
2
+ class Saml
3
+ require 'rack/saml/metadata/abstract_metadata'
4
+ autoload "OneloginMetadata", 'rack/saml/metadata/onelogin_metadata'
5
+ autoload "OpensamlMetadata", 'rack/saml/metadata/opensaml_metadata'
6
+
7
+ class MetadataHandler
8
+ attr_reader :sp_metadata
9
+
10
+ # Rack::Saml::MetadataHandler
11
+ # request: Rack current request instance
12
+ # saml_config: config/saml.yml
13
+ # metadata: specified idp entity in the config/metadata.yml
14
+ def initialize(request, saml_config, metadata)
15
+ @sp_metadata = (eval "Rack::Saml::#{saml_config['assertion_handler'].to_s.capitalize}Metadata").new(request, saml_config, metadata)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ module Rack
2
+ class Saml
3
+ module OneloginSetting
4
+ require 'ruby-saml'
5
+
6
+ def saml_settings
7
+ settings = Onelogin::Saml::Settings.new
8
+ settings.assertion_consumer_service_url = "#{@request.scheme}://#{@request.host}#{":#{@request.port}" if @request.port}#{request.script_name}#{@saml_config['protected_path']}"
9
+ settings.issuer = @saml_config['saml_sp']
10
+ settings.idp_sso_target_url = @metadata['saml2_http_redirect']
11
+ settings.idp_cert = @metadata['certificate']
12
+ settings.name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
13
+ #settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
14
+ settings
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ module Rack
2
+ class Saml
3
+ class AbstractRequest
4
+ attr_reader :request, :saml_config, :metadata
5
+
6
+ def initialize(request, saml_config, metadata)
7
+ @request = request
8
+ @saml_config = saml_config
9
+ @metadata = metadata
10
+ end
11
+
12
+ def redirect_uri
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ module Rack
2
+ class Saml
3
+ require 'rack/saml/misc/onelogin_setting'
4
+
5
+ class OneloginRequest < AbstractRequest
6
+ include OneloginSetting
7
+
8
+ def initialize(request, saml_config, metadata)
9
+ super(request, saml_config, metadata)
10
+ @authrequest = Onelogin::Saml::Authrequest.new
11
+ end
12
+
13
+ def redirect_uri
14
+ @authrequest.create(saml_settings)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ module Rack
2
+ class Saml
3
+ class OpensamlRequest < AbstractRequest
4
+ # To be implemented
5
+ def initialize(request, saml_config, metadata)
6
+ end
7
+
8
+ def redirect_uri
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,19 @@
1
+ module Rack
2
+ class Saml
3
+ require 'rack/saml/request/abstract_request'
4
+ autoload "OneloginRequest", 'rack/saml/request/onelogin_request'
5
+ autoload "OpensamlRequest", 'rack/saml/request/opensaml_request'
6
+
7
+ class RequestHandler
8
+ attr_reader :authn_request
9
+
10
+ # Rack::Saml::RequestHandler
11
+ # request: Rack current request instance
12
+ # saml_config: config/saml.yml
13
+ # metadata: specified idp entity in the config/metadata.yml
14
+ def initialize(request, saml_config, metadata)
15
+ @authn_request = (eval "Rack::Saml::#{saml_config['assertion_handler'].to_s.capitalize}Request").new(request, saml_config, metadata)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ module Rack
2
+ class Saml
3
+ class AbstractResponse
4
+ attr_reader :request, :saml_config, :metadata
5
+
6
+ def initialize(request, saml_config, metadata)
7
+ @request = request
8
+ @saml_config = saml_config
9
+ @metadata = metadata
10
+ end
11
+
12
+ def is_valid?
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ module Rack
2
+ class Saml
3
+ require 'rack/saml/misc/onelogin_setting'
4
+
5
+ class OneloginResponse < AbstractResponse
6
+ include OneloginSetting
7
+ #extend Forwardable
8
+
9
+ def initialize(request, saml_config, metadata)
10
+ super(request, saml_config, metadata)
11
+ @response = Onelogin::Saml::Response.new(@request.params['SAMLResponse'])
12
+ @response.settings = saml_settings
13
+ end
14
+
15
+ def is_valid?
16
+ @response.is_valid?
17
+ end
18
+
19
+ def attributes
20
+ @response.attributes
21
+ end
22
+
23
+ #def_delegator :@response, :is_valid?, :attributes
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ module Rack
2
+ class Saml
3
+ class OpensamlResponse < AbstractResponse
4
+ # To be implemented
5
+ def initialize(request, saml_config, metadata)
6
+ end
7
+
8
+ def redirect_uri
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,29 @@
1
+ module Rack
2
+ class Saml
3
+ require 'rack/saml/response/abstract_response'
4
+ autoload "OneloginResponse", 'rack/saml/response/onelogin_response'
5
+ autoload "OpensamlResponse", 'rack/saml/response/opensaml_response'
6
+
7
+ class ResponseHandler
8
+ attr_reader :response
9
+
10
+ # Rack::Saml::ResponseHandler
11
+ # request: Rack current request instance
12
+ # saml_config: config/saml.yml
13
+ # metadata: specified idp entity in the config/metadata.yml
14
+ def initialize(request, saml_config, metadata)
15
+ @response = (eval "Rack::Saml::#{saml_config['assertion_handler'].to_s.capitalize}Response").new(request, saml_config, metadata)
16
+ end
17
+
18
+ def extract_attrs(env, attribute_map, opts = {})
19
+ attribute_map.each do |attr_name, env_name|
20
+ attribute = @response.attributes[attr_name]
21
+ env[env_name] = attribute if !attribute.nil?
22
+ end
23
+ if !opts[:shib_app_id].nil?
24
+ env['Shib-Application-ID'] = opts[:shib_app_id]
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
data/rack-saml.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/rack-saml/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.add_dependency 'ruby-saml', '~> 0.4.7'
6
+
7
+ gem.authors = ["Toyokazu Akiyama"]
8
+ gem.email = ["toyokazu@gmail.com"]
9
+ gem.description = %q{SAML middleware for Rack (using ruby-saml)}
10
+ gem.summary = %q{SAML middleware for Rack (using ruby-saml)}
11
+ gem.homepage = ""
12
+
13
+ gem.files = `find . -not \\( -regex ".*\\.git.*" -o -regex "\\./pkg.*" -o -regex "\\./spec.*" \\)`.split("\n").map{ |f| f.gsub(/^.\//, '') }
14
+ #gem.files = `find .`.split("\n").map{ |f| f.gsub(/^.\//, '') }
15
+ gem.test_files = `find spec/*`.split("\n")
16
+ #gem.test_files = `find test/* spec/* features/*`.split("\n")
17
+ gem.name = "rack-saml"
18
+ gem.require_paths = ["lib"]
19
+ gem.version = Rack::Saml::VERSION
20
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-saml
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Toyokazu Akiyama
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-29 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ruby-saml
16
+ requirement: &2153190660 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.4.7
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2153190660
25
+ description: SAML middleware for Rack (using ruby-saml)
26
+ email:
27
+ - toyokazu@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - bin/conv_metadata.rb
33
+ - config/attribute-map.yml
34
+ - config/attribute-map.yml.sample
35
+ - config/metadata.yml
36
+ - config/saml.yml
37
+ - Gemfile
38
+ - lib/rack/saml/metadata/abstract_metadata.rb
39
+ - lib/rack/saml/metadata/onelogin_metadata.rb
40
+ - lib/rack/saml/metadata/opensaml_metadata.rb
41
+ - lib/rack/saml/metadata_handler.rb
42
+ - lib/rack/saml/misc/onelogin_setting.rb
43
+ - lib/rack/saml/request/abstract_request.rb
44
+ - lib/rack/saml/request/onelogin_request.rb
45
+ - lib/rack/saml/request/opensaml_request.rb
46
+ - lib/rack/saml/request_handler.rb
47
+ - lib/rack/saml/response/abstract_response.rb
48
+ - lib/rack/saml/response/onelogin_response.rb
49
+ - lib/rack/saml/response/opensaml_response.rb
50
+ - lib/rack/saml/response_handler.rb
51
+ - lib/rack/saml.rb
52
+ - lib/rack-saml/version.rb
53
+ - lib/rack-saml.rb
54
+ - rack-saml.gemspec
55
+ - Rakefile
56
+ - README.md
57
+ homepage: ''
58
+ licenses: []
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubyforge_project:
77
+ rubygems_version: 1.8.12
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: SAML middleware for Rack (using ruby-saml)
81
+ test_files: []
82
+ has_rdoc: