auth_scope 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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/Gemfile +9 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +103 -0
- data/Rakefile +10 -0
- data/auth_scope.gemspec +24 -0
- data/lib/auth_scope.rb +46 -0
- data/lib/auth_scope/version.rb +3 -0
- data/spec/auth_scope_spec.rb +92 -0
- data/spec/spec_helper.rb +24 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d9a769a8c4cd7cce6e3a9b2526a1d5c6f8242b91
|
4
|
+
data.tar.gz: 94f0698f3d63887e2612c0c108814329e6979d33
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5ba43d4cc8eb9da44324f7a1f8a17b0d38a6d231e2b415bc437298fa8f3b7db39598b8f1e23e9cffaf62446d1b889ffc7952ba092a07e23dccb63c9142a08410
|
7
|
+
data.tar.gz: 96fde064c62572ff4a885ada8b9f8e7af9ec43a404eb189b936bfc9b9294bcee4c1d734b545ed7ec09f428273705e0fc27726c2444061a8b2292bc1bd5d39840
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Michael Bleigh
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# AuthScope
|
2
|
+
|
3
|
+
AuthScope is a simple library for describing authorization patterns through strings.
|
4
|
+
It is intended to work in conjunction with (for example) an OAuth 2.0 API.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'auth_scope'
|
11
|
+
|
12
|
+
## Scope Structure
|
13
|
+
|
14
|
+
Authorization scope strings look something like this:
|
15
|
+
|
16
|
+
user:email apps:one,another:read profile:*
|
17
|
+
|
18
|
+
Spaces delimit individual authorization grants and can be treated as
|
19
|
+
completely independent of one another. When specified together they are
|
20
|
+
considered to authorize additively, meaning that authorization is granted
|
21
|
+
if *any* of the provided scopes are matched.
|
22
|
+
|
23
|
+
The `:` or **namespace** delimiter lets you create generic and specific
|
24
|
+
permissions. `user:email` is more specific than `user` for instance.
|
25
|
+
|
26
|
+
The `,` or **batch** delimiter allows you to apply a scope to multiple
|
27
|
+
resouces at the same namespace level. `user:email,avatar` is equivalent
|
28
|
+
to asking for a scope of `user:email user:avatar`.
|
29
|
+
|
30
|
+
The `*` or **wildcard** is used to indicate permissiveness for a single
|
31
|
+
namespace. For instance, `user:*` would grant access to `user:email`
|
32
|
+
but not `user:email:write`.
|
33
|
+
|
34
|
+
The `**` or **global wildcard** is used to indicate permissiveness for
|
35
|
+
any number of namespaces. `**` would grant access to **everything**, and
|
36
|
+
`user:**:write` would grant access to any `write` permissions, even if
|
37
|
+
deeply nested.
|
38
|
+
|
39
|
+
The more namespaces a scope has, the greater permission it authorizes.
|
40
|
+
Each segment of a namespace is considered to be granted, so a scope of
|
41
|
+
`user:email` grants both `user` and `user:email` scopes.
|
42
|
+
|
43
|
+
## Usage
|
44
|
+
|
45
|
+
In simple terms, you will initialize an AuthScope with a string and query
|
46
|
+
its authorization using the `can?` method:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
require 'auth_scope'
|
50
|
+
|
51
|
+
scope = AuthScope.new("user:email apps:foo,bar:*")
|
52
|
+
|
53
|
+
scope.can? "user:email" # => true
|
54
|
+
scope.can? "user:write" # => false
|
55
|
+
scope.can? "apps:foo:write" # => true
|
56
|
+
```
|
57
|
+
|
58
|
+
You may also specify an array of authorization strings. This is treated no
|
59
|
+
differently than space delimiting:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
scope = AuthScope.new("user:email", "apps:foo,bar:*")
|
63
|
+
```
|
64
|
+
|
65
|
+
The `any?` method tests a set of potential authorizations to see if any match:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
scope = AuthScope.new("admin")
|
69
|
+
scope.any? "user:email", "admin" # => true
|
70
|
+
```
|
71
|
+
|
72
|
+
The `all? method tests that each of a set of authorizations is a match:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
scope = AuthScope.new("admin")
|
76
|
+
scope.all? "user:email", "admin" # => false
|
77
|
+
```
|
78
|
+
|
79
|
+
### Object Scopes
|
80
|
+
|
81
|
+
It will often be useful for an object to be able to describe its own scope.
|
82
|
+
AuthScope will call `to_scope` on passed-in arguments before checking them.
|
83
|
+
For example:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
class User
|
87
|
+
def to_scope
|
88
|
+
"user:#{id}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
scope = AuthScope.new("user:123:write")
|
93
|
+
scope.can? user, "write" # => true
|
94
|
+
scope.can? another_user, "write" # => false
|
95
|
+
```
|
96
|
+
|
97
|
+
## Contributing
|
98
|
+
|
99
|
+
1. Fork it ( https://github.com/divshot/auth_scope/fork )
|
100
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
101
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
102
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
103
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/auth_scope.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'auth_scope/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "auth_scope"
|
8
|
+
spec.version = AuthScope::VERSION
|
9
|
+
spec.authors = ["Michael Bleigh"]
|
10
|
+
spec.email = ["mbleigh@mbleigh.com"]
|
11
|
+
spec.summary = %q{Authorization library using namespaced strings. Great for OAuth 2.0 APIs.}
|
12
|
+
spec.description = %q{Authorization library using namespaced strings. Great for OAuth 2.0 APIs.}
|
13
|
+
spec.homepage = "https://github.com/divshot/auth_scope"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
end
|
data/lib/auth_scope.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
class AuthScope
|
2
|
+
def initialize(*scopes)
|
3
|
+
@tree = {}
|
4
|
+
scopes.join(" ").split(" ").each{|s| add(s) }
|
5
|
+
end
|
6
|
+
|
7
|
+
def add(scope_string)
|
8
|
+
roots = [@tree]
|
9
|
+
|
10
|
+
scope_string.split(":").each do |parts|
|
11
|
+
roots = parts.split(",").map do |part|
|
12
|
+
roots.map{|r| r[part.to_s] ||= {}; r[part.to_s]}
|
13
|
+
end.flatten
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def can?(*args)
|
18
|
+
subject = subject.respond_to?(:to_scope) ? subject.to_scope : subject.to_s
|
19
|
+
parts = args.map{|arg| (arg.respond_to?(:to_scope) ? arg.to_scope : arg.to_s).split(":") }.flatten.map(&:to_s)
|
20
|
+
roots = [@tree]
|
21
|
+
parts.each do |part|
|
22
|
+
return true if roots.detect{|r| r.key?('**')}
|
23
|
+
is_last = part == parts.last
|
24
|
+
if roots.detect{|r| r.key?('*')}
|
25
|
+
roots = roots.map{|r| [r[part], r['*']].compact }.flatten
|
26
|
+
else
|
27
|
+
return false unless roots.detect{|r| r.key?(part)}
|
28
|
+
roots = roots.map{|r| r[part] }.flatten
|
29
|
+
end
|
30
|
+
end
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def any?(*scopes)
|
35
|
+
scopes = scopes.join(" ").split(" ")
|
36
|
+
!!scopes.detect{|scope| can?(scope)}
|
37
|
+
end
|
38
|
+
|
39
|
+
def all?(*scopes)
|
40
|
+
scopes = scopes.join(" ").split(" ")
|
41
|
+
scopes.each{|scope| return false unless can?(scope) }
|
42
|
+
true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
require "auth_scope/version"
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AuthScope do
|
4
|
+
subject{ AuthScope.new(@scope || "user:email") }
|
5
|
+
|
6
|
+
describe '#can?' do
|
7
|
+
it 'should authorize for any segment of a chain' do
|
8
|
+
@scope = "foo:bar:baz"
|
9
|
+
should_authorize "foo"
|
10
|
+
should_authorize "foo:bar"
|
11
|
+
should_authorize "foo:bar:baz"
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should not work for a non-specified subsegment' do
|
15
|
+
@scope = "user:read"
|
16
|
+
should_not_authorize "user:write"
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should not work for a deeper namespace' do
|
20
|
+
@scope = "user"
|
21
|
+
should_not_authorize "user:write"
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should work from multiple defined scopes' do
|
25
|
+
@scope = "user apps"
|
26
|
+
should_authorize "user"
|
27
|
+
should_authorize "apps"
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'wildcards' do
|
31
|
+
it 'should allow wildcards to represent any scope' do
|
32
|
+
@scope = "user:*"
|
33
|
+
should_authorize "user:email"
|
34
|
+
should_authorize "user:write"
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should not grant multi-level authorization with wildcards' do
|
38
|
+
@scope = "user:*"
|
39
|
+
should_not_authorize "user:write:another"
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should work with lower namespaces and wildcards' do
|
43
|
+
@scope = "user:*:write"
|
44
|
+
should_authorize "user:bob:write"
|
45
|
+
should_authorize "user:frank:write"
|
46
|
+
should_not_authorize "user:bob:read"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'batch delimiter' do
|
51
|
+
it 'should work for each specified in the batch' do
|
52
|
+
@scope = "user,app:write"
|
53
|
+
should_authorize "user:write"
|
54
|
+
should_authorize "app:write"
|
55
|
+
should_not_authorize "book:write"
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should work on multiple levels' do
|
59
|
+
@scope = "user,app:read,write"
|
60
|
+
should_authorize "user:write"
|
61
|
+
should_authorize "app:read"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'global wildcards' do
|
66
|
+
it 'should work for any level of specificity' do
|
67
|
+
@scope = "user:**"
|
68
|
+
should_authorize "user:abc:write:foo"
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should work as a catch-all if supplied alone' do
|
72
|
+
@scope = "**"
|
73
|
+
should_authorize "foo:bar:baz"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#any?' do
|
79
|
+
it 'should authorize any of the supplied scopes' do
|
80
|
+
@scope = "user:foo:write"
|
81
|
+
expect(subject).to be_any('user:foo:write', 'admin')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#all?' do
|
86
|
+
it 'should authorize only with ALL of the supplied scopes' do
|
87
|
+
@scope = "user admin"
|
88
|
+
expect(subject).to be_all("user", "admin")
|
89
|
+
expect(subject).not_to be_all("user", "bork")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'auth_scope'
|
2
|
+
|
3
|
+
module SpecHelpers
|
4
|
+
def should_authorize(scope)
|
5
|
+
expect(subject).to be_can(scope)
|
6
|
+
end
|
7
|
+
|
8
|
+
def should_not_authorize(scope)
|
9
|
+
expect(subject).not_to be_can(scope)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
15
|
+
config.run_all_when_everything_filtered = true
|
16
|
+
config.filter_run :focus
|
17
|
+
|
18
|
+
# Run specs in random order to surface order dependencies. If you find an
|
19
|
+
# order dependency and want to debug it, you can fix the order by providing
|
20
|
+
# the seed, which is printed after each run.
|
21
|
+
# --seed 1234
|
22
|
+
config.order = 'random'
|
23
|
+
config.include SpecHelpers
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: auth_scope
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Bleigh
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-05-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
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: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Authorization library using namespaced strings. Great for OAuth 2.0 APIs.
|
56
|
+
email:
|
57
|
+
- mbleigh@mbleigh.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- .gitignore
|
63
|
+
- .rspec
|
64
|
+
- Gemfile
|
65
|
+
- Guardfile
|
66
|
+
- LICENSE.txt
|
67
|
+
- README.md
|
68
|
+
- Rakefile
|
69
|
+
- auth_scope.gemspec
|
70
|
+
- lib/auth_scope.rb
|
71
|
+
- lib/auth_scope/version.rb
|
72
|
+
- spec/auth_scope_spec.rb
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
homepage: https://github.com/divshot/auth_scope
|
75
|
+
licenses:
|
76
|
+
- MIT
|
77
|
+
metadata: {}
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - '>='
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - '>='
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
requirements: []
|
93
|
+
rubyforge_project:
|
94
|
+
rubygems_version: 2.0.14
|
95
|
+
signing_key:
|
96
|
+
specification_version: 4
|
97
|
+
summary: Authorization library using namespaced strings. Great for OAuth 2.0 APIs.
|
98
|
+
test_files:
|
99
|
+
- spec/auth_scope_spec.rb
|
100
|
+
- spec/spec_helper.rb
|
101
|
+
has_rdoc:
|