swt_federation 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.rdoc +39 -0
- data/Rakefile +1 -0
- data/lib/swt_federation.rb +101 -0
- data/lib/swt_federation/version.rb +3 -0
- data/swt_federation.gemspec +26 -0
- metadata +64 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
= SWT Federation for Ruby
|
2
|
+
|
3
|
+
This library provides SWT-based federation for Ruby applications (ruby 1.9)
|
4
|
+
|
5
|
+
The entry point of this library is the TokenHandler class. Before using it you have to set some class instance
|
6
|
+
variables that hold configuration information.
|
7
|
+
|
8
|
+
|
9
|
+
TokenHandler.realm = 'http://0.0.0.0:8080/login/'
|
10
|
+
TokenHandler.token_type = 'http://schemas.xmlsoap.org/ws/2009/11/swt-token-profile-1.0'
|
11
|
+
TokenHandler.issuer = 'https://southworksinc.accesscontrol.windows.net/'
|
12
|
+
TokenHandler.toke_key = 'your key here'
|
13
|
+
|
14
|
+
The lines above can be placed in the config.ru file.
|
15
|
+
Below you can find an example of how to use it with Sinatra.
|
16
|
+
|
17
|
+
before do
|
18
|
+
|
19
|
+
next if request.path_info == '/swt'
|
20
|
+
if(session['user']==nil)
|
21
|
+
redirect ("<your_identity_provider_url>")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
post '/swt' do
|
26
|
+
response = TokenHandler.new(params[:wresult])
|
27
|
+
if (response.is_valid?)
|
28
|
+
session['user'] = response.claims['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress']
|
29
|
+
target_url = params[:wctx]
|
30
|
+
if (target_url != nil)
|
31
|
+
redirect(target_url)
|
32
|
+
else
|
33
|
+
redirect('/home')
|
34
|
+
end
|
35
|
+
else
|
36
|
+
status(403)
|
37
|
+
halt('access denied')
|
38
|
+
end
|
39
|
+
end
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html
|
2
|
+
require "swt_federation/version"
|
3
|
+
|
4
|
+
require 'nokogiri'
|
5
|
+
require 'time'
|
6
|
+
require 'base64'
|
7
|
+
require 'cgi'
|
8
|
+
require 'openssl'
|
9
|
+
|
10
|
+
|
11
|
+
module SwtFederation
|
12
|
+
|
13
|
+
class TokenHandler
|
14
|
+
class << self; attr_accessor :realm, :issuer, :token_key, :token_type end
|
15
|
+
|
16
|
+
attr_reader :validation_errors, :claims
|
17
|
+
|
18
|
+
def initialize(wresult)
|
19
|
+
@validation_errors = []
|
20
|
+
@claims={}
|
21
|
+
@wresult=Nokogiri::XML(wresult)
|
22
|
+
|
23
|
+
parse_response()
|
24
|
+
end
|
25
|
+
|
26
|
+
def is_valid?
|
27
|
+
@validation_errors.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
#parse through the document, performing validation & pulling out claims
|
31
|
+
def parse_response
|
32
|
+
parse_address()
|
33
|
+
parse_expires()
|
34
|
+
parse_token_type()
|
35
|
+
parse_token()
|
36
|
+
end
|
37
|
+
|
38
|
+
#does the address field have the expected address?
|
39
|
+
def parse_address
|
40
|
+
address = get_element('//t:RequestSecurityTokenResponse/wsp:AppliesTo/addr:EndpointReference/addr:Address')
|
41
|
+
@validation_errors << "Address field is empty." and return if address.nil?
|
42
|
+
@validation_errors << "Address field is incorrect." unless address == self.class.realm
|
43
|
+
end
|
44
|
+
|
45
|
+
#is the expire value valid?
|
46
|
+
def parse_expires
|
47
|
+
expires = get_element('//t:RequestSecurityTokenResponse/t:Lifetime/wsu:Expires')
|
48
|
+
@validation_errors << "Expiration field is empty." and return if expires.nil?
|
49
|
+
@validation_errors << "Invalid format for expiration field." and return unless /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[0-1]|0[1-9]|[1-2][0-9])T(2[0-3]|[0-1][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[0-1][0-9]):[0-5][0-9])?$/.match(expires)
|
50
|
+
@validation_errors << "Expiration date occurs in the past." unless Time.now.utc.iso8601 < Time.iso8601(expires).iso8601
|
51
|
+
end
|
52
|
+
|
53
|
+
#is the token type what we expected?
|
54
|
+
def parse_token_type
|
55
|
+
token_type = get_element('//t:RequestSecurityTokenResponse/t:TokenType')
|
56
|
+
@validation_errors << "TokenType field is empty." and return if token_type.nil?
|
57
|
+
@validation_errors << "Invalid token type." unless token_type == self.class.token_type
|
58
|
+
end
|
59
|
+
|
60
|
+
#parse the binary token
|
61
|
+
def parse_token
|
62
|
+
binary_token = get_element('//t:RequestSecurityTokenResponse/t:RequestedSecurityToken/wsse:BinarySecurityToken')
|
63
|
+
@validation_errors << "No binary token exists." and return if binary_token.nil?
|
64
|
+
|
65
|
+
decoded_token = Base64.decode64(binary_token)
|
66
|
+
name_values={}
|
67
|
+
decoded_token.split('&').each do |entry|
|
68
|
+
pair=entry.split('=')
|
69
|
+
name_values[CGI.unescape(pair[0]).chomp] = CGI.unescape(pair[1]).chomp
|
70
|
+
end
|
71
|
+
|
72
|
+
@validation_errors << "Response token is expired." if Time.now.to_i > name_values["ExpiresOn"].to_i
|
73
|
+
@validation_errors << "Invalid token issuer." unless name_values["Issuer"]=="#{self.class.issuer}"
|
74
|
+
@validation_errors << "Invalid audience." unless name_values["Audience"] =="#{self.class.realm}"
|
75
|
+
|
76
|
+
# is HMAC valid?
|
77
|
+
token_hmac = decoded_token.split("&HMACSHA256=")
|
78
|
+
swt=token_hmac[0]
|
79
|
+
@validation_errors << "HMAC does not match computed value." unless name_values['HMACSHA256'] == Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'),Base64.decode64(self.class.token_key),swt)).chomp
|
80
|
+
|
81
|
+
# remove non-claims from collection and make claims available
|
82
|
+
|
83
|
+
@claims = name_values.reject {|key, value| !key.include? '/claims/'}
|
84
|
+
end
|
85
|
+
|
86
|
+
#given an path, return the content of the first matching element
|
87
|
+
def get_element(xpath_statement)
|
88
|
+
begin
|
89
|
+
@wresult.xpath(xpath_statement,
|
90
|
+
't'=>'http://schemas.xmlsoap.org/ws/2005/02/trust',
|
91
|
+
'wsu'=>'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd',
|
92
|
+
'wsp'=>'http://schemas.xmlsoap.org/ws/2004/09/policy',
|
93
|
+
'wsse'=>'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd',
|
94
|
+
'addr'=>'http://www.w3.org/2005/08/addressing')[0].content
|
95
|
+
rescue
|
96
|
+
nil
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "swt_federation/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "swt_federation"
|
7
|
+
s.version = SwtFederation::VERSION
|
8
|
+
s.authors = ["NicoPaez"]
|
9
|
+
s.email = ["nicopaez@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{This gem provides website federation based on Simple Web Tokens (SWT)}
|
12
|
+
s.description = %q{It is inspired on this post (http://blogs.msdn.com/b/silverlining/archive/2011/10/03/ruby-web-sites-and-windows-azure-appfabric-access-control.aspx) by Larry Franks.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "swt_federation"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
#s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency "rspec"
|
23
|
+
# s.add_runtime_dependency "rest-client"
|
24
|
+
s.add_dependency "nokogiri"
|
25
|
+
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: swt_federation
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- NicoPaez
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-21 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: nokogiri
|
16
|
+
requirement: &11839000 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *11839000
|
25
|
+
description: It is inspired on this post (http://blogs.msdn.com/b/silverlining/archive/2011/10/03/ruby-web-sites-and-windows-azure-appfabric-access-control.aspx)
|
26
|
+
by Larry Franks.
|
27
|
+
email:
|
28
|
+
- nicopaez@gmail.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- .gitignore
|
34
|
+
- Gemfile
|
35
|
+
- README.rdoc
|
36
|
+
- Rakefile
|
37
|
+
- lib/swt_federation.rb
|
38
|
+
- lib/swt_federation/version.rb
|
39
|
+
- swt_federation.gemspec
|
40
|
+
homepage: ''
|
41
|
+
licenses: []
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project: swt_federation
|
60
|
+
rubygems_version: 1.8.10
|
61
|
+
signing_key:
|
62
|
+
specification_version: 3
|
63
|
+
summary: This gem provides website federation based on Simple Web Tokens (SWT)
|
64
|
+
test_files: []
|