sinatra-els 1.0.0
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/README.md +113 -0
- data/lib/sinatra/els.rb +101 -0
- data/spec/app.rb +23 -0
- data/spec/app_spec.rb +42 -0
- data/spec/spec_helper.rb +20 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ba66d68fec95409ea3060da53d24672033ed54d8
|
4
|
+
data.tar.gz: 316f7cc35abd3ccc780a3b68622c241e5bb1e7ec
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3c1507750818d74b9bfd6bbfa26a760d8c4aa9c625fecb7f45e871889f7d21749fb4019c71da38ec0acf6c0cd3b5f6ded728c34255da1d1a14ccc29ceb91ae37
|
7
|
+
data.tar.gz: 8edab93b0fb5d522cf167dab07fbac928fda1bfc2cd7f3d7e69be153624ea885ed6b38ff87950ab9204064025fcfe23023dec1cdd92f26d57083815bea14976e
|
data/README.md
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
# Sinatra ELS Helper
|
2
|
+
|
3
|
+
This is a Sinatra extension to assist in verification of an ELS (openAM) authentication token.
|
4
|
+
|
5
|
+
These tokens are usually stored in a browser cookie to assist in SSO but we can also use them
|
6
|
+
to create Web Services with federated authentication. The caller must first authenticate
|
7
|
+
with ELS to generate a token then pass that token along with a WS call (usually in a HTTP Header)
|
8
|
+
|
9
|
+
To find out more about generating a token, see [https://stash.ops.aol.com/projects/CIO_CODEHAUS_PUBLIC/repos/els_token/browse]
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Bung this in your Gemfile
|
14
|
+
|
15
|
+
gem 'sinatra-els', '~>1.0.0'
|
16
|
+
|
17
|
+
|
18
|
+
There are two flavors of Sinatra applications, classic and modular. Both need a require statement:
|
19
|
+
|
20
|
+
require 'sinatra/els'
|
21
|
+
|
22
|
+
That's it for classic apps. Modular apps must register the extension:
|
23
|
+
|
24
|
+
class MyApp < Sinatra::Base
|
25
|
+
register Sinatra::ELS
|
26
|
+
end
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
There are a couple of ways to use this extension:
|
31
|
+
|
32
|
+
1. els_before hook
|
33
|
+
|
34
|
+
The els_before hook, without any arguments, will setup a Sinatra before handler for all routes and interrogate for ELS authentication.
|
35
|
+
|
36
|
+
the els_before method takes an optional hash which can specify an :only or :except key with an array of strings that represent regaulr expressions (without the enclosing '/' slashes). These will be used to match the route and invoke ELS authentication. As the key names suggest, regular expressions passed to the :only key will only invoke ELS authentication when the current route matches. Converseley, the :except key will be used to ignore ELS authentication for the supplied regex's.
|
37
|
+
|
38
|
+
Example 1: Only process ELS authentication on the '/api/' route and any route ending with 'reports'
|
39
|
+
|
40
|
+
els_before :only => [ "^\/api\/", "reports$" ]
|
41
|
+
|
42
|
+
Example 2: Perform ELS authentication on all routes except '/util/'
|
43
|
+
|
44
|
+
els_before :except => [ "^\/util\/" ]
|
45
|
+
|
46
|
+
|
47
|
+
2. authorize!
|
48
|
+
|
49
|
+
The _authorize!_ method (used by els_before) can be manually invoked in any route should the els_before hook not be required or if the default behavior of els_before doesn't meet your needs
|
50
|
+
|
51
|
+
Example:
|
52
|
+
|
53
|
+
get '/myapi' do
|
54
|
+
authorize!
|
55
|
+
# Code below will be executed if authentication is successful
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
###Environment specific setup
|
60
|
+
|
61
|
+
This is just something I have found useful during development and testing. Because the default is to NOT use ELS, it's possible to selectively setup ELS depending on your environment.
|
62
|
+
|
63
|
+
Example: Only use the els_before hook in production and uat environments
|
64
|
+
|
65
|
+
configure :production, :uat do
|
66
|
+
els_before
|
67
|
+
end
|
68
|
+
|
69
|
+
### Configuration
|
70
|
+
|
71
|
+
This extension requires a configuration object (as a hash) which will tell it the ELS end point to use, the AD Users and/or AD Groups to allow when performing credential validation and the HTTP header key in which to expect an ELS identity token. For the sake of this example I have stored my config in a .yml file
|
72
|
+
|
73
|
+
__yml file: /config/els.yml__
|
74
|
+
|
75
|
+
development:
|
76
|
+
uri: https://els-sso.corp.aol.com/opensso/identity
|
77
|
+
users:
|
78
|
+
- bob
|
79
|
+
- alice
|
80
|
+
groups:
|
81
|
+
- MyApi users
|
82
|
+
- Domain Admins
|
83
|
+
header: X-API-Key
|
84
|
+
|
85
|
+
This must be loaded, under the environment key, to :els_opts.
|
86
|
+
|
87
|
+
The following is a full example
|
88
|
+
|
89
|
+
__app file: /app/my_api.rb___
|
90
|
+
|
91
|
+
class MyApi < Sinatra::Base
|
92
|
+
|
93
|
+
set :root, File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
94
|
+
|
95
|
+
register Sinatra::ELS
|
96
|
+
|
97
|
+
set :els_opts, YAML.load_file(File.join(settings.root, 'config','els.yml'))[settings.environment.to_s]
|
98
|
+
|
99
|
+
configure :production, :uat do
|
100
|
+
els_before :except => [ "\/util/" ]
|
101
|
+
end
|
102
|
+
|
103
|
+
# ELS validation will occur before this route in production and uat
|
104
|
+
get '/api/coolness' do
|
105
|
+
"Cool! You are allowed to use this"
|
106
|
+
end
|
107
|
+
|
108
|
+
# ELS validation will NOT occur before this route in any environment
|
109
|
+
get '/util/health' do
|
110
|
+
"All good, carry on"
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
data/lib/sinatra/els.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'els_token'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
|
6
|
+
module Sinatra
|
7
|
+
module ELS
|
8
|
+
|
9
|
+
module Auth
|
10
|
+
#
|
11
|
+
# Set up the before hook to use ELS authentication.
|
12
|
+
# Setup ELS options using <i>set :els_opts</i>
|
13
|
+
#
|
14
|
+
# params:
|
15
|
+
# opts - Optional hash with a key of either :only, or :except
|
16
|
+
# and an array of String values which will be cast into
|
17
|
+
# regular expressions.
|
18
|
+
#
|
19
|
+
# example:
|
20
|
+
# # only use els when patch matches "/api/" or anything ending in "/orders"
|
21
|
+
# els_before true, :only => ["^\/api\/",".*\/orders$"]
|
22
|
+
#
|
23
|
+
# # do not use els if the "/util/" path is matched
|
24
|
+
# els_before true, :except
|
25
|
+
#
|
26
|
+
# # use els before everything
|
27
|
+
# els_before
|
28
|
+
#
|
29
|
+
# when <i>before_all === false</i> all before processing will be ignored
|
30
|
+
#
|
31
|
+
# An optional hash of path matchers can be supplied
|
32
|
+
#
|
33
|
+
def els_before(opts = {})
|
34
|
+
before {
|
35
|
+
if opts.key? :only
|
36
|
+
# execute authorize if path matches regex(s)
|
37
|
+
if request.path_info.match(/#{opts[:only].map{|i|"(#{i})"}.join('|')}/)
|
38
|
+
authorize!
|
39
|
+
end
|
40
|
+
elsif opts.key? :except
|
41
|
+
# execute authorize if path doesn't match regex(s)
|
42
|
+
if not request.path_info.match(/#{opts[:except].map{|i|"(#{i})"}.join('|')}/)
|
43
|
+
authorize!
|
44
|
+
end
|
45
|
+
else
|
46
|
+
authorize!
|
47
|
+
end
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module Helpers
|
53
|
+
|
54
|
+
#
|
55
|
+
# Perform ELS authentication
|
56
|
+
# Setup ELS options using <i>set :els_opts</i>
|
57
|
+
#
|
58
|
+
def authorize!
|
59
|
+
token = env[settings.els_opts['header']]
|
60
|
+
headers "X-Resource" => request.request_method + " : " + request.url
|
61
|
+
unless token
|
62
|
+
logger.warn("Missing #{settings.els_opts['header']} from IP Address: #{env['REMOTE_ADDR']}")
|
63
|
+
halt 403
|
64
|
+
else
|
65
|
+
unless ElsToken.is_token_valid?(token, settings.els_opts)
|
66
|
+
logger.warn("failed authentication from IP Address: #{env['REMOTE_ADDR']}")
|
67
|
+
halt 403
|
68
|
+
else
|
69
|
+
user = ElsToken.get_identity(token, settings.els_opts)
|
70
|
+
# Ensure user has explicit permission via username or group association
|
71
|
+
skip_user = settings.els_opts['users'].nil?
|
72
|
+
skip_group = settings.els_opts['groups'].nil?
|
73
|
+
user_missing = group_missing = false
|
74
|
+
unless skip_user
|
75
|
+
user_missing = !settings.els_opts['users'].include?(user.name)
|
76
|
+
end
|
77
|
+
unless skip_group
|
78
|
+
group_missing = (settings.els_opts['groups'] & user.roles).empty?
|
79
|
+
end
|
80
|
+
if user_missing and group_missing
|
81
|
+
logger.warn("#{user.name} Does not have permission to use this servce: #{env['REMOTE_ADDR']}")
|
82
|
+
halt 403
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# TODO - other interesting els routes
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.registered(app)
|
92
|
+
app.helpers ELS::Helpers
|
93
|
+
app.register ELS::Auth
|
94
|
+
|
95
|
+
# When including, set the els_opts hash. eg.
|
96
|
+
#app.set :els_opts, YAML.load_file(File.join(app.settings.root, 'config','els.yml'))[app.settings.environment.to_s]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
register ELS
|
101
|
+
end
|
data/spec/app.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'logger'
|
3
|
+
require 'sinatra/els'
|
4
|
+
|
5
|
+
class Tester < Sinatra::Base
|
6
|
+
set :root, File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
7
|
+
|
8
|
+
register Sinatra::ELS
|
9
|
+
|
10
|
+
set :els_opts, YAML.load_file(File.join(settings.root, 'config','els.yml'))[settings.environment.to_s]
|
11
|
+
|
12
|
+
els_before :only => ["^\/auth"]
|
13
|
+
|
14
|
+
get '/no_auth' do
|
15
|
+
"It works!"
|
16
|
+
end
|
17
|
+
|
18
|
+
get '/auth' do
|
19
|
+
"It works!"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
data/spec/app_spec.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'els_token'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
describe Tester do
|
6
|
+
include Rack::Test::Methods
|
7
|
+
|
8
|
+
before :all do
|
9
|
+
# obtain a valid els token
|
10
|
+
puts "Please provide a valid user which is contained in the els.yml file"
|
11
|
+
username = gets.chomp
|
12
|
+
puts "Please enter your password"
|
13
|
+
password = STDIN.noecho(&:gets).chomp
|
14
|
+
els = YAML.load_file(File.join(File.dirname(__FILE__),'..', 'config','els.yml'))["test"]
|
15
|
+
@token = ElsToken.authenticate(username,password,els)
|
16
|
+
end
|
17
|
+
|
18
|
+
def app
|
19
|
+
Tester
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'ELS Not Required' do
|
23
|
+
it 'should not return 403' do
|
24
|
+
get '/no_auth'
|
25
|
+
expect(last_response).to be_ok
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'ELS Required' do
|
30
|
+
|
31
|
+
it 'should return 403 with no els token' do
|
32
|
+
get '/auth'
|
33
|
+
expect(last_response.status).to eql(403)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should return 200 with els token' do
|
37
|
+
get '/auth', nil, 'X-API-Key' => "#{@token}"
|
38
|
+
expect(last_response).to be_ok
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
ENV['RACK_ENV'] = 'test'
|
2
|
+
|
3
|
+
app_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
4
|
+
$: << File.join(app_root, 'lib')
|
5
|
+
|
6
|
+
require 'app'
|
7
|
+
require 'rspec'
|
8
|
+
require 'rack/test'
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
12
|
+
config.run_all_when_everything_filtered = true
|
13
|
+
config.filter_run :focus
|
14
|
+
|
15
|
+
# Run specs in random order to surface order dependencies. If you find an
|
16
|
+
# order dependency and want to debug it, you can fix the order by providing
|
17
|
+
# the seed, which is printed after each run.
|
18
|
+
# --seed 1234
|
19
|
+
config.order = 'random'
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sinatra-els
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Neil Chambers
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-10-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rack-test
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: els_token
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.2.3
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.2.3
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sinatra
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.4.4
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.4.4
|
69
|
+
description: See README.md
|
70
|
+
email: neil.chambers@teamaol.com
|
71
|
+
executables: []
|
72
|
+
extensions: []
|
73
|
+
extra_rdoc_files:
|
74
|
+
- README.md
|
75
|
+
files:
|
76
|
+
- lib/sinatra/els.rb
|
77
|
+
- README.md
|
78
|
+
- spec/app.rb
|
79
|
+
- spec/app_spec.rb
|
80
|
+
- spec/spec_helper.rb
|
81
|
+
homepage: https://stash.ops.aol.com/projects/CIOCODEHAUS/repos/sinatra-els/browse
|
82
|
+
licenses:
|
83
|
+
- MIT
|
84
|
+
metadata: {}
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options:
|
87
|
+
- --title
|
88
|
+
- Sinatra ELS Helper
|
89
|
+
- --main
|
90
|
+
- README.md
|
91
|
+
- --line-numbers
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
requirements: []
|
105
|
+
rubyforge_project:
|
106
|
+
rubygems_version: 2.1.1
|
107
|
+
signing_key:
|
108
|
+
specification_version: 4
|
109
|
+
summary: ELS helper for Sinatra
|
110
|
+
test_files:
|
111
|
+
- spec/app.rb
|
112
|
+
- spec/app_spec.rb
|
113
|
+
- spec/spec_helper.rb
|