saml_camel 0.1.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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +68 -0
- data/Rakefile +34 -0
- data/app/assets/config/saml_camel_manifest.js +2 -0
- data/app/assets/javascripts/saml_camel/application.js +13 -0
- data/app/assets/stylesheets/saml_camel/application.css +15 -0
- data/app/assets/stylesheets/scaffold.css +80 -0
- data/app/controllers/concerns/saml_camel/saml_helpers.rb +30 -0
- data/app/controllers/saml_camel/application_controller.rb +5 -0
- data/app/controllers/saml_camel/saml_controller.rb +48 -0
- data/app/helpers/saml_camel/application_helper.rb +4 -0
- data/app/jobs/saml_camel/application_job.rb +4 -0
- data/app/mailers/saml_camel/application_mailer.rb +6 -0
- data/app/models/saml_camel/application_record.rb +5 -0
- data/app/models/saml_camel/transaction.rb +69 -0
- data/app/views/layouts/saml_camel/application.html.erb +14 -0
- data/config/routes.rb +5 -0
- data/lib/saml_camel.rb +5 -0
- data/lib/saml_camel/engine.rb +16 -0
- data/lib/saml_camel/version.rb +3 -0
- data/lib/tasks/saml_camel_tasks.rake +155 -0
- metadata +121 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: db6a8d7294f375eadf0c0358d7e438fb367a2b45
|
4
|
+
data.tar.gz: c89095973afa1aaa726f242cabb7b2d360360f33
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ba097413cf902c8bd2184c64e4fcb87b39027cf223042952732a5022245901196edafd7e3b9c20d9f9e76b76c95eb23bf1eda86f2b3d5c35d9ba814e5b5ad280
|
7
|
+
data.tar.gz: afb5650b0e054af8cb911461b7211106eddb305d994e77c9941738f03f6c085a6a0fa8fe1a05ca1c0fe0d561214ac0304cfeac3bb97a880cee5fd0423b9b7ff7
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2018 Danai Adkisson
|
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,68 @@
|
|
1
|
+
# SamlCamel - (not production ready)
|
2
|
+
## Installation
|
3
|
+
Add this line to your application's Gemfile:
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
source "https://gems-internal.oit.duke.edu" do
|
7
|
+
gem 'saml_camel'
|
8
|
+
end
|
9
|
+
```
|
10
|
+
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
```bash
|
14
|
+
$ bundle
|
15
|
+
```
|
16
|
+
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
1. run `rake saml_camel:generate_saml` to generate metadata files for each environment. you can also specify a specifc/custom environment like this `rake saml_camel:generate_saml environment=acceptance`
|
20
|
+
|
21
|
+
**Note: these steps will use development as an example, if you use seperate metadata per environemtn, you will repeat each step for your chosed environement**
|
22
|
+
|
23
|
+
2. from the root of your app open `saml/development/settings.json` and specify an entity ID of your choice. this is a unique identifier used by the
|
24
|
+
Identity Provider(idp) to recognize your app. Typically it should take the form of a url, however note that it is just an identifier and does not have to be routeable (e.g. https://my-app-name/not/a/real/route)
|
25
|
+
|
26
|
+
3. Go to https://idms-web.oit.duke.edu/spreg/sps and register your metadata with the identity provider. You will need the values from `saml/development/settings.json` in addition to the `saml/development/saml_certificate.crt`
|
27
|
+
|
28
|
+
- copy the entity_id you chose in the `settings.json` file and paste it into the "Entity Field"
|
29
|
+
- fill out functional purpose, responsible dept, function owner dept, and audience with information relevent to your application
|
30
|
+
- copy the cert from `saml/development/saml_certificate.crt` and paste it into the Certificate Field
|
31
|
+
- copy the acs value and paste it into the Location field in the Assertion Consumer Service box
|
32
|
+
- note that the default host value for ACS is `http://locahost:3000` which is the default `rails s` host. If you're using a differnet host (such as in production or using docker) you will want to replace the host value with what is relevent for your situation(*e.g. https://my-app.duke.edu/saml/consumeSaml*), but keep the path `/saml/consumeSaml`
|
33
|
+
|
34
|
+
4. In your app mount the engine in config/routes.rb
|
35
|
+
```ruby
|
36
|
+
mount SamlCamel::Engine, at: "/saml"
|
37
|
+
```
|
38
|
+
|
39
|
+
5. include saml helpers in `app/controllers/application_controller.rb`
|
40
|
+
```ruby
|
41
|
+
class ApplicationController < ActionController::Base
|
42
|
+
include SamlCamel::SamlHelpers
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
6. now simply provide the `saml protect` method in your controllers (via `around_action`) to protect paths
|
47
|
+
**NOTE: it is important you MUST use around_action**
|
48
|
+
|
49
|
+
7. to logout simply make a post to `localhost:3000/saml/logout`. This will kill the local saml session, and the session with the identity provider. You can specify a return url in `saml/development/settings.json`
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
class DashboardController < ApplicationController
|
53
|
+
around_action :saml_protect, except: [:home]
|
54
|
+
|
55
|
+
def home
|
56
|
+
end
|
57
|
+
|
58
|
+
def index
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
7. response attributes found in `session[:saml_attributes]`
|
65
|
+
|
66
|
+
|
67
|
+
## License
|
68
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'SamlCamel'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
load 'rails/tasks/statistics.rake'
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
require 'bundler/gem_tasks'
|
24
|
+
|
25
|
+
require 'rake/testtask'
|
26
|
+
|
27
|
+
Rake::TestTask.new(:test) do |t|
|
28
|
+
t.libs << 'test'
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
30
|
+
t.verbose = false
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
task default: :test
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,80 @@
|
|
1
|
+
body {
|
2
|
+
background-color: #fff;
|
3
|
+
color: #333;
|
4
|
+
margin: 33px;
|
5
|
+
}
|
6
|
+
|
7
|
+
body, p, ol, ul, td {
|
8
|
+
font-family: verdana, arial, helvetica, sans-serif;
|
9
|
+
font-size: 13px;
|
10
|
+
line-height: 18px;
|
11
|
+
}
|
12
|
+
|
13
|
+
pre {
|
14
|
+
background-color: #eee;
|
15
|
+
padding: 10px;
|
16
|
+
font-size: 11px;
|
17
|
+
}
|
18
|
+
|
19
|
+
a {
|
20
|
+
color: #000;
|
21
|
+
}
|
22
|
+
|
23
|
+
a:visited {
|
24
|
+
color: #666;
|
25
|
+
}
|
26
|
+
|
27
|
+
a:hover {
|
28
|
+
color: #fff;
|
29
|
+
background-color: #000;
|
30
|
+
}
|
31
|
+
|
32
|
+
th {
|
33
|
+
padding-bottom: 5px;
|
34
|
+
}
|
35
|
+
|
36
|
+
td {
|
37
|
+
padding: 0 5px 7px;
|
38
|
+
}
|
39
|
+
|
40
|
+
div.field,
|
41
|
+
div.actions {
|
42
|
+
margin-bottom: 10px;
|
43
|
+
}
|
44
|
+
|
45
|
+
#notice {
|
46
|
+
color: green;
|
47
|
+
}
|
48
|
+
|
49
|
+
.field_with_errors {
|
50
|
+
padding: 2px;
|
51
|
+
background-color: red;
|
52
|
+
display: table;
|
53
|
+
}
|
54
|
+
|
55
|
+
#error_explanation {
|
56
|
+
width: 450px;
|
57
|
+
border: 2px solid red;
|
58
|
+
padding: 7px 7px 0;
|
59
|
+
margin-bottom: 20px;
|
60
|
+
background-color: #f0f0f0;
|
61
|
+
}
|
62
|
+
|
63
|
+
#error_explanation h2 {
|
64
|
+
text-align: left;
|
65
|
+
font-weight: bold;
|
66
|
+
padding: 5px 5px 5px 15px;
|
67
|
+
font-size: 12px;
|
68
|
+
margin: -7px -7px 0;
|
69
|
+
background-color: #c00;
|
70
|
+
color: #fff;
|
71
|
+
}
|
72
|
+
|
73
|
+
#error_explanation ul li {
|
74
|
+
font-size: 12px;
|
75
|
+
list-style: square;
|
76
|
+
}
|
77
|
+
|
78
|
+
label {
|
79
|
+
display: block;
|
80
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_dependency "saml_camel/application_controller"
|
2
|
+
|
3
|
+
module SamlCamel::SamlHelpers
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
#this generates a call to the idp, which will then be returned to the consume action the in saml_contorller
|
7
|
+
def saml_request(host_request)
|
8
|
+
request = OneLogin::RubySaml::Authrequest.new
|
9
|
+
cookies.encrypted[:saml_camel_redirect] = host_request.url
|
10
|
+
redirect_to(request.create(SamlCamel::Transaction.saml_settings))
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def saml_reset
|
15
|
+
session[:saml_success] = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def saml_protect
|
20
|
+
begin
|
21
|
+
saml_request(request) unless (session[:saml_success] || session[:sp_session]) #keeps us from looping, and maintains sp session
|
22
|
+
yield
|
23
|
+
ensure
|
24
|
+
saml_reset #keeps us from looping
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require_dependency "saml_camel/application_controller"
|
2
|
+
|
3
|
+
module SamlCamel
|
4
|
+
class SamlController < ApplicationController
|
5
|
+
skip_before_action :verify_authenticity_token ,only: [:consume,:logout]
|
6
|
+
|
7
|
+
|
8
|
+
#TODO ROUTABLE STUFF GOES IN THE SHIB CONTROLLER, METHODS CALLED BUT NOT ROUTED GO TO SAML_CONTROLLER
|
9
|
+
def index
|
10
|
+
@attributes = session[:saml_attributes]
|
11
|
+
end
|
12
|
+
|
13
|
+
def consume
|
14
|
+
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], :settings => saml_settings)
|
15
|
+
response.settings = saml_settings
|
16
|
+
|
17
|
+
if response.is_valid? # validate the SAML Response
|
18
|
+
# authorize_success, log the user
|
19
|
+
session[:saml_success] = true
|
20
|
+
session[:sp_session] = true
|
21
|
+
session[:saml_attributes] = SamlCamel::Transaction.map_attributes(response.attributes)
|
22
|
+
|
23
|
+
#TODO account for nil redirect
|
24
|
+
redirect_to cookies.encrypted[:saml_camel_redirect]
|
25
|
+
else # otherwise list out the errors in the response
|
26
|
+
#TODO how do we handle errors?
|
27
|
+
session[:saml_success] = false
|
28
|
+
response.errors
|
29
|
+
redirect_to main_app.try('root_path')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def logout
|
34
|
+
session[:saml_attributes] = nil
|
35
|
+
session[:sp_session] = nil
|
36
|
+
|
37
|
+
return_url = SamlCamel::Transaction.logout #this methods logs the user out of the IDP, and returns a url to be redirected to
|
38
|
+
redirect_to return_url
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
private
|
43
|
+
def saml_settings
|
44
|
+
SamlCamel::Transaction.saml_settings
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module SamlCamel
|
2
|
+
class Transaction
|
3
|
+
SP_SETTINGS = JSON.parse(File.read("saml/#{Rails.env}/settings.json"))
|
4
|
+
|
5
|
+
def self.saml_settings
|
6
|
+
sp_settings = SP_SETTINGS["settings"]
|
7
|
+
settings = OneLogin::RubySaml::Settings.new
|
8
|
+
settings.assertion_consumer_service_url = sp_settings["acs"]
|
9
|
+
|
10
|
+
|
11
|
+
settings.issuer = sp_settings["entity_id"]
|
12
|
+
settings.idp_sso_target_url = sp_settings["sso_url"]
|
13
|
+
|
14
|
+
|
15
|
+
#certificate to register with IDP and key to decrypt
|
16
|
+
settings.certificate = File.read("saml/#{Rails.env}/saml_certificate.crt")
|
17
|
+
|
18
|
+
#certificate to decrypt SAML response
|
19
|
+
settings.private_key = File.read("saml/#{Rails.env}/saml_key.key")
|
20
|
+
|
21
|
+
#certificate to verify IDP signature
|
22
|
+
settings.idp_cert = File.read("saml/#{Rails.env}/idp_certificate.crt")
|
23
|
+
|
24
|
+
# Optional for most SAML IdPs
|
25
|
+
settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
|
26
|
+
settings.attribute_consuming_service.configure do
|
27
|
+
service_name "Service"
|
28
|
+
service_index 5
|
29
|
+
add_attribute :redirect_path => "root_path"
|
30
|
+
end
|
31
|
+
settings
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.map_attributes(attrs)
|
35
|
+
attr_map = SP_SETTINGS["attribute_map"]
|
36
|
+
mapped_attributes = {}
|
37
|
+
|
38
|
+
attrs.each do |attr,value|
|
39
|
+
mapped_name = attr_map[attr]
|
40
|
+
if mapped_name.nil? #handles attributes not in map
|
41
|
+
mapped_attributes[attr] = value
|
42
|
+
else
|
43
|
+
mapped_attributes[mapped_name] = value
|
44
|
+
end
|
45
|
+
end
|
46
|
+
mapped_attributes
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def self.logout
|
51
|
+
url = URI("https://shib.oit.duke.edu/cgi-bin/logout.pl")
|
52
|
+
|
53
|
+
http = Net::HTTP.new(url.host, url.port)
|
54
|
+
http.use_ssl = true
|
55
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
56
|
+
|
57
|
+
request = Net::HTTP::Post.new(url)
|
58
|
+
request["authorization"] = 'Basic c29hcC5pZG1zLm9pdDowRGFvdXU2Y1g4MEJ1Vkg2QlFaaA=='
|
59
|
+
request["content-type"] = 'application/x-www-form-urlencoded'
|
60
|
+
request["cache-control"] = 'no-cache'
|
61
|
+
request.body = "logoutWithoutPrompt=1"
|
62
|
+
|
63
|
+
response = http.request(request)
|
64
|
+
|
65
|
+
logout_return = SP_SETTINGS["settings"]["logout_return_url"]
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Saml camel</title>
|
5
|
+
<%= stylesheet_link_tag "saml_camel/application", media: "all" %>
|
6
|
+
<%= javascript_include_tag "saml_camel/application" %>
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
|
11
|
+
<%= yield %>
|
12
|
+
|
13
|
+
</body>
|
14
|
+
</html>
|
data/config/routes.rb
ADDED
data/lib/saml_camel.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'onelogin/ruby-saml'
|
3
|
+
|
4
|
+
module SamlCamel
|
5
|
+
class Engine < ::Rails::Engine
|
6
|
+
isolate_namespace SamlCamel
|
7
|
+
|
8
|
+
config.to_prepare do
|
9
|
+
|
10
|
+
Dir.glob(Rails.root + "app/decorators/**/*_decorator*.rb").each do |c|
|
11
|
+
require_dependency(c)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
namespace :saml_camel do
|
2
|
+
desc "Generate Files for Saml"
|
3
|
+
task :generate_saml do
|
4
|
+
dir = "#{Rails.root}/saml/"
|
5
|
+
FileUtils.mkdir(dir) unless Dir.exists?(dir)
|
6
|
+
|
7
|
+
specified_env = ENV['environment']
|
8
|
+
default_envs = ["production","test","development"]
|
9
|
+
key = generate_key
|
10
|
+
cert = generate_cert(key)
|
11
|
+
settings = generate_saml_settings.to_json
|
12
|
+
|
13
|
+
#TODO pull in specified idp certificate
|
14
|
+
# idp_cert = File.read("saml/idp_certs/#{ENV['idp']}.crt") if ENV['idp']
|
15
|
+
idp_cert = """MIIEWjCCA0KgAwIBAgIJAP1rB/FjRgy6MA0GCSqGSIb3DQEBBQUAMHsxCzAJBgNV
|
16
|
+
BAYTAlVTMRcwFQYDVQQIEw5Ob3J0aCBDYXJvbGluYTEPMA0GA1UEBxMGRHVyaGFt
|
17
|
+
MRgwFgYDVQQKEw9EdWtlIFVuaXZlcnNpdHkxDDAKBgNVBAsTA09JVDEaMBgGA1UE
|
18
|
+
AxMRc2hpYi5vaXQuZHVrZS5lZHUwHhcNMTAwOTA5MTI0NDU1WhcNMjgwOTA0MTI0
|
19
|
+
NDU1WjB7MQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExDzAN
|
20
|
+
BgNVBAcTBkR1cmhhbTEYMBYGA1UEChMPRHVrZSBVbml2ZXJzaXR5MQwwCgYDVQQL
|
21
|
+
EwNPSVQxGjAYBgNVBAMTEXNoaWIub2l0LmR1a2UuZWR1MIIBIjANBgkqhkiG9w0B
|
22
|
+
AQEFAAOCAQ8AMIIBCgKCAQEAt+hnl6gSRi0Y8VuNl6PCPYejj7VfVs/y8bRa5zAY
|
23
|
+
RHwb75+vBSs2j1yeUcSore9Ba5Ni7v947V34afRMGRPOqr4TEDZxU+1Bg0zAvSrR
|
24
|
+
n4Y8B+zyJuhtOpmOZzTwE9o/Oc+CB4kYV/K0woKZdcoxHJm8TbqBqdxU4fFYUlNU
|
25
|
+
o4Dr5jRdCSr9MHBOqGWXtQMg16qYNB7StNk4twY29FNnpZwkVTfsE76uVsRMkG8i
|
26
|
+
6/RiHpXZ/ioOOqndptbEGdsOIE3ivAJOZdvYwnDe5NnTH06P01HsxH3OOnYqhuG2
|
27
|
+
J6qdhqoelGeHRG+jfl8YkYXCcKQvja2tJ5G+6iqSN7DP6QIDAQABo4HgMIHdMB0G
|
28
|
+
A1UdDgQWBBQHYXwB6otkfyMOmUI59j8823hFRDCBrQYDVR0jBIGlMIGigBQHYXwB
|
29
|
+
6otkfyMOmUI59j8823hFRKF/pH0wezELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5v
|
30
|
+
cnRoIENhcm9saW5hMQ8wDQYDVQQHEwZEdXJoYW0xGDAWBgNVBAoTD0R1a2UgVW5p
|
31
|
+
dmVyc2l0eTEMMAoGA1UECxMDT0lUMRowGAYDVQQDExFzaGliLm9pdC5kdWtlLmVk
|
32
|
+
dYIJAP1rB/FjRgy6MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAG7q
|
33
|
+
wJpiSLJbx2gj/cGDYeuBW/CeRGNghjQ/mb076P3WXsRNPAimcXulSUbQkS6eDH4t
|
34
|
+
Ifvsa0jf4FRsEOwH/x8354/0wyv4RwuavX25kjpmoFn3O+eKokyzsc7/Q2gsm0mv
|
35
|
+
V8XQo+5b+4we8AFYlAVp26nLeIqAiJM8xZJ9yHuzVL1O4yxIWIKECWHLqY5+1nas
|
36
|
+
XNiLURrHhsK5pZUPLuhzJFgZuJT62TtnrjJXlrRhJ389VSkh6R64C6ncjNkg6/Cu
|
37
|
+
tA6SX0infqNRyPRNJK+bnQd1yOP4++tjD/lAPE+5tiD/waI3fArt43ZE/qp7pYMS
|
38
|
+
9TEfyQ5QpfRYAUFWXBc=
|
39
|
+
"""
|
40
|
+
|
41
|
+
unless specified_env
|
42
|
+
default_envs.each do |e|
|
43
|
+
dir = "#{Rails.root}/saml/#{e}"
|
44
|
+
FileUtils.mkdir(dir) unless Dir.exists?(dir)
|
45
|
+
File.open("#{Rails.root}/saml/#{e}/saml_certificate.crt","w+") {|f| f.write(cert) }
|
46
|
+
File.open("#{Rails.root}/saml/#{e}/saml_key.key","w+") {|f| f.write(key) }
|
47
|
+
File.open("#{Rails.root}/saml/#{e}/idp_certificate.crt","w+") {|f| f.write(idp_cert) }
|
48
|
+
File.open("#{Rails.root}/saml/#{e}/settings.json","w+") {|f| f.write(settings) }
|
49
|
+
end
|
50
|
+
else
|
51
|
+
dir = "#{Rails.root}/saml/#{specified_env}"
|
52
|
+
FileUtils.mkdir(dir) unless Dir.exists?(dir)
|
53
|
+
File.open("#{Rails.root}/saml/#{specified_env}/saml_certificate.crt","w+") {|f| f.write(cert) }
|
54
|
+
File.open("#{Rails.root}/saml/#{specified_env}/saml_key.key","w+") {|f| f.write(key) }
|
55
|
+
File.open("#{Rails.root}/saml/#{specified_env}/idp_certificate.crt","w+") {|f| f.write(idp_cert) }
|
56
|
+
File.open("#{Rails.root}/saml/#{specified_env}/settings.json","w+") {|f| f.write(settings) }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
def generate_saml_settings
|
66
|
+
{
|
67
|
+
_comment: "note you will need to restart the application when you make changes to this file",
|
68
|
+
settings: {
|
69
|
+
acs: "http://localhost:3000/saml/consumeSaml" ,
|
70
|
+
entity_id: "https://your-entity-id.com",
|
71
|
+
sso_url: "https://shib.oit.duke.edu/idp/profile/SAML2/Redirect/SSO",
|
72
|
+
logout_return_url: "http://localhost:3000"
|
73
|
+
},
|
74
|
+
"attribute_map": {
|
75
|
+
"urn:oid:1.3.6.1.4.1.5923.1.1.1.9": "eduPersonScopedAffiliation",
|
76
|
+
"urn:oid:1.3.6.1.4.1.5923.1.1.1.6": "eduPersonPrincipalName",
|
77
|
+
"urn:oid:2.5.4.3": "cn",
|
78
|
+
"urn:oid:0.9.2342.19200300.100.1.1": "uid",
|
79
|
+
"urn:oid:0.9.2342.19200300.100.1.3": "mail",
|
80
|
+
"urn:oid:1.3.6.1.4.1.5923.1.1.1.5": "eduPersonPrimaryAffiliation",
|
81
|
+
"urn:oid:2.16.840.1.113730.3.1.241": "displayName",
|
82
|
+
"urn:mace:duke.edu:idms:unique-id": "duDukeID",
|
83
|
+
"urn:mace:duke.edu:idms:dku-id": "urn:mace:duke.edu:idms:dku-id",
|
84
|
+
"urn:oid:1.3.6.1.4.1.5923.1.5.1.1": "isMemberOf",
|
85
|
+
"urn:oid:2.5.4.42": "givenName",
|
86
|
+
"urn:oid:2.5.4.4": "sn",
|
87
|
+
"urn:oid:2.5.4.11": "ou",
|
88
|
+
"urn:oid:1.3.6.1.4.1.5923.1.1.1.1": "eduPersonAffiliation",
|
89
|
+
"urn:oid:2.5.4.20": "telephoneNumber",
|
90
|
+
"urn:oid:2.5.4.12": "title",
|
91
|
+
"urn:mace:duke.edu:idms:middle-name1": "duMiddleName1",
|
92
|
+
"urn:mace:duke.edu:idms:sap:name-first": "duSAPNameFirst",
|
93
|
+
"urn:mace:duke.edu:idms:sap:name-last": "duSAPNameLast",
|
94
|
+
"urn:mace:duke.edu:idms:proxy-token": "duProxyToken"
|
95
|
+
}
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def generate_key
|
101
|
+
OpenSSL::PKey::RSA.new(2048)
|
102
|
+
end
|
103
|
+
|
104
|
+
def generate_cert(key)
|
105
|
+
STDOUT.puts "Country Name (2 letter code) [AU]:"
|
106
|
+
country = STDIN.gets.strip
|
107
|
+
|
108
|
+
STDOUT.puts "State or Province Name (full name) [Some-State]:"
|
109
|
+
state = STDIN.gets.strip
|
110
|
+
|
111
|
+
STDOUT.puts "Locality Name (eg, city):"
|
112
|
+
city = STDIN.gets.strip
|
113
|
+
|
114
|
+
STDOUT.puts "Organization Name (eg, company):"
|
115
|
+
org = STDIN.gets.strip
|
116
|
+
|
117
|
+
STDOUT.puts "Organizational Unit Name (eg, section):"
|
118
|
+
unit = STDIN.gets.strip
|
119
|
+
|
120
|
+
STDOUT.puts "Common Name (non url name, remember this is not a server cert):"
|
121
|
+
cn = STDIN.gets.strip
|
122
|
+
|
123
|
+
STDOUT.puts "Email Address:"
|
124
|
+
email = STDIN.gets.strip
|
125
|
+
|
126
|
+
|
127
|
+
public_key = key.public_key
|
128
|
+
|
129
|
+
#generate subject line of cert
|
130
|
+
subject = "/C=#{country}/ST=#{state}/L=#{city}/O=#{org}/OU=#{unit}/CN=#{cn}/emailAddress=#{email}"
|
131
|
+
|
132
|
+
cert = OpenSSL::X509::Certificate.new
|
133
|
+
cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject) #TODO this line breaks when https:// is added for CN
|
134
|
+
cert.not_before = Time.now
|
135
|
+
cert.not_after = Time.now + 365 * 24 * 60 * 60
|
136
|
+
cert.public_key = public_key
|
137
|
+
cert.serial = 0x0
|
138
|
+
cert.version = 2
|
139
|
+
|
140
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
141
|
+
ef.subject_certificate = cert
|
142
|
+
ef.issuer_certificate = cert
|
143
|
+
cert.extensions = [
|
144
|
+
ef.create_extension("basicConstraints","CA:TRUE", true),
|
145
|
+
ef.create_extension("subjectKeyIdentifier", "hash"),
|
146
|
+
# ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
|
147
|
+
]
|
148
|
+
cert.add_extension ef.create_extension("authorityKeyIdentifier",
|
149
|
+
"keyid:always,issuer:always")
|
150
|
+
|
151
|
+
cert.sign key, OpenSSL::Digest::SHA256.new
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
end
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: saml_camel
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- 'Danai Adkisson '
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-04-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ruby-saml
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.7.2
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.7.2
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: ruby-saml
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.7.2
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.7.2
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: byebug
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: SAML tool wrapping onelogin/rubysaml
|
70
|
+
email:
|
71
|
+
- da129@duke.edu
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- MIT-LICENSE
|
77
|
+
- README.md
|
78
|
+
- Rakefile
|
79
|
+
- app/assets/config/saml_camel_manifest.js
|
80
|
+
- app/assets/javascripts/saml_camel/application.js
|
81
|
+
- app/assets/stylesheets/saml_camel/application.css
|
82
|
+
- app/assets/stylesheets/scaffold.css
|
83
|
+
- app/controllers/concerns/saml_camel/saml_helpers.rb
|
84
|
+
- app/controllers/saml_camel/application_controller.rb
|
85
|
+
- app/controllers/saml_camel/saml_controller.rb
|
86
|
+
- app/helpers/saml_camel/application_helper.rb
|
87
|
+
- app/jobs/saml_camel/application_job.rb
|
88
|
+
- app/mailers/saml_camel/application_mailer.rb
|
89
|
+
- app/models/saml_camel/application_record.rb
|
90
|
+
- app/models/saml_camel/transaction.rb
|
91
|
+
- app/views/layouts/saml_camel/application.html.erb
|
92
|
+
- config/routes.rb
|
93
|
+
- lib/saml_camel.rb
|
94
|
+
- lib/saml_camel/engine.rb
|
95
|
+
- lib/saml_camel/version.rb
|
96
|
+
- lib/tasks/saml_camel_tasks.rake
|
97
|
+
homepage: https://oit.duke.edu
|
98
|
+
licenses:
|
99
|
+
- MIT
|
100
|
+
metadata: {}
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 2.5.1
|
118
|
+
signing_key:
|
119
|
+
specification_version: 4
|
120
|
+
summary: SAML tool wrapping onelogin/rubysaml
|
121
|
+
test_files: []
|