sinatra-els 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|