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.
@@ -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
@@ -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
@@ -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
@@ -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
+
@@ -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
@@ -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