yauth 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +1 -0
- data/.document +5 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +122 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/bin/yauth +5 -0
- data/examples/sinatra/app.rb +20 -0
- data/examples/sinatra/config/users.yml +4 -0
- data/lib/yauth.rb +17 -0
- data/lib/yauth/cli.rb +34 -0
- data/lib/yauth/failure_app.rb +15 -0
- data/lib/yauth/strategy.rb +24 -0
- data/lib/yauth/user.rb +31 -0
- data/lib/yauth/user_manager.rb +45 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/yauth/cli_spec.rb +46 -0
- data/spec/yauth/failure_app_spec.rb +41 -0
- data/spec/yauth/strategy_spec.rb +54 -0
- data/spec/yauth/user_manager_spec.rb +141 -0
- data/spec/yauth/user_spec.rb +95 -0
- data/spec/yauth_spec.rb +17 -0
- data/yauth.gemspec +88 -0
- metadata +174 -0
data/.autotest
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'test_notifier/runner/autotest'
|
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm --create use ruby-1.8.7-p302@yauth
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Matteo Collina
|
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.rdoc
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
= Yauth
|
2
|
+
|
3
|
+
Yauth is a extremely simple authentication solution for prototypes, and provides a drop-in solution for http basic authentication.
|
4
|
+
It uses a yaml file to store usernames and hashed password combined with the http basic authentication.
|
5
|
+
The whole gem provides a better-than-nothing security, and it was designed with small prototypes in mind.
|
6
|
+
It is entirely developed as a https://github.com/hassox/warden strategy, the same library on which the popular Devise is based.
|
7
|
+
|
8
|
+
Yauth resolves one well defined problem: protecting a web application that is going to be accessed by a few users.
|
9
|
+
As adding new users requires manual intervention (only launching a command, see below), it's a good choice for single-users web applications or prototypes. In this way you can password-protect your app easily and expose it to your clients on the web.
|
10
|
+
|
11
|
+
== Installation
|
12
|
+
|
13
|
+
First, install the gem:
|
14
|
+
|
15
|
+
gem install yauth
|
16
|
+
|
17
|
+
Or if you are using Bundler:
|
18
|
+
|
19
|
+
echo "gem 'yauth'" >> Gemfile
|
20
|
+
bundle install
|
21
|
+
|
22
|
+
Add the first user (username 'bar' and password 'foo'):
|
23
|
+
|
24
|
+
yauth add bar foo
|
25
|
+
|
26
|
+
Check the 'config/users.yml' into your version control system:
|
27
|
+
|
28
|
+
git add config/users.yml
|
29
|
+
git commit -m "Added user 'bar'."
|
30
|
+
|
31
|
+
== Configuration
|
32
|
+
|
33
|
+
Yauth requires almost no configuration, 5 lines of code.
|
34
|
+
|
35
|
+
=== Sinatra
|
36
|
+
|
37
|
+
In your application.rb, simply put:
|
38
|
+
|
39
|
+
Yauth::Strategy.install!
|
40
|
+
|
41
|
+
use Warden::Manager do |manager|
|
42
|
+
manager.default_strategies :yauth_users
|
43
|
+
manager.failure_app = Yauth::FailureApp.new("Your Realm")
|
44
|
+
end
|
45
|
+
|
46
|
+
To require authentication inside an action, simply call:
|
47
|
+
|
48
|
+
get '/' do
|
49
|
+
request.env['warden'].authenticate! # execution stop and auth is required
|
50
|
+
"hello world\n"
|
51
|
+
end
|
52
|
+
|
53
|
+
=== Rails 3
|
54
|
+
|
55
|
+
In your config/application.rb file put:
|
56
|
+
|
57
|
+
Yauth::Strategy.install!
|
58
|
+
|
59
|
+
config.middleware.use Warden::Manager do |manager|
|
60
|
+
manager.default_strategies :yauth_users
|
61
|
+
manager.failure_app = Yauth::FailureApp.new("Your Realm")
|
62
|
+
end
|
63
|
+
|
64
|
+
To require authentication inside an action, add this method to app/controllers/application_controller.rb:
|
65
|
+
|
66
|
+
private
|
67
|
+
def authenticate
|
68
|
+
env['warden'].authenticate!
|
69
|
+
end
|
70
|
+
|
71
|
+
Then, you can use the +authenticate+ method as a before filter for every method that requires authentication:
|
72
|
+
|
73
|
+
before_filter :authenticate, :only => :your_method_here
|
74
|
+
|
75
|
+
== Adding and removing users
|
76
|
+
|
77
|
+
If you want to add the user 'foo', with password 'bar', just launch:
|
78
|
+
|
79
|
+
yauth add foo bar
|
80
|
+
|
81
|
+
Then, if you want to change 'foo' password to 'oof', just launch:
|
82
|
+
|
83
|
+
yauht add foo oof
|
84
|
+
|
85
|
+
Finally, to remove 'foo' user:
|
86
|
+
|
87
|
+
yauth rm foo
|
88
|
+
|
89
|
+
== Security Considerations
|
90
|
+
|
91
|
+
Users are stored in the 'config/users.yml' file, with the password stored as SHA256 hash.
|
92
|
+
In this way it's safe to add the 'config/users.yml' to the version control system.
|
93
|
+
|
94
|
+
You can see an example of the 'config/users.yml' file:
|
95
|
+
|
96
|
+
---
|
97
|
+
- user:
|
98
|
+
username: admin
|
99
|
+
password: 8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918
|
100
|
+
|
101
|
+
|
102
|
+
== TODO
|
103
|
+
|
104
|
+
Future versions will include:
|
105
|
+
* drop-in api key solution, i.e. user might have a key for API prototypation;
|
106
|
+
* authentication scopes, as defined in warden.
|
107
|
+
|
108
|
+
== Contributing to yauth
|
109
|
+
|
110
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
111
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
112
|
+
* Fork the project
|
113
|
+
* Start a feature/bugfix branch
|
114
|
+
* Commit and push until you are happy with your contribution
|
115
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
116
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
117
|
+
|
118
|
+
== Copyright
|
119
|
+
|
120
|
+
Copyright (c) 2011 Matteo Collina. See LICENSE.txt for
|
121
|
+
further details.
|
122
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
require 'jeweler'
|
5
|
+
Jeweler::Tasks.new do |gem|
|
6
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
7
|
+
gem.name = "yauth"
|
8
|
+
gem.homepage = "http://github.com/mcollina/yauth"
|
9
|
+
gem.license = "MIT"
|
10
|
+
gem.summary = %Q{A drop-in authentication solution for prototypes.}
|
11
|
+
gem.description = %Q{Yauth is a extremely simple authentication solution for prototipes, developed as a warden strategy. It uses a yaml file to store usernames and hashed password. It provides a better-than-nothing security.}
|
12
|
+
gem.email = "matteo@matteocollina.com"
|
13
|
+
gem.authors = ["Matteo Collina"]
|
14
|
+
gem.add_runtime_dependency 'warden', '~> 1.0'
|
15
|
+
gem.add_runtime_dependency 'thor', '~> 0.14.0'
|
16
|
+
gem.add_development_dependency 'test_notifier', '~> 0.3.6'
|
17
|
+
gem.add_development_dependency 'autotest', '~> 4.4'
|
18
|
+
gem.add_development_dependency 'rcov'
|
19
|
+
end
|
20
|
+
Jeweler::RubygemsDotOrgTasks.new
|
21
|
+
|
22
|
+
require 'rspec/core'
|
23
|
+
require 'rspec/core/rake_task'
|
24
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
25
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
26
|
+
end
|
27
|
+
|
28
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
29
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
30
|
+
spec.rcov = true
|
31
|
+
spec.rcov_opts = ["--text-summary", "--exclude","lib\/rspec,bin\/rspec,lib\/rcov," +
|
32
|
+
"spec,diff-lcs,thor,warden,rack"]
|
33
|
+
end
|
34
|
+
|
35
|
+
task :default => :spec
|
36
|
+
|
37
|
+
require 'rake/rdoctask'
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
39
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
40
|
+
|
41
|
+
rdoc.rdoc_dir = 'rdoc'
|
42
|
+
rdoc.title = "yauth #{version}"
|
43
|
+
rdoc.rdoc_files.include('README*')
|
44
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
45
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/bin/yauth
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'sinatra'
|
3
|
+
require File.join(File.dirname(__FILE__), "..", "..", "lib", "yauth")
|
4
|
+
|
5
|
+
configure do
|
6
|
+
enable :sessions #needed by warden
|
7
|
+
end
|
8
|
+
|
9
|
+
Yauth::Strategy.install!
|
10
|
+
|
11
|
+
use Warden::Manager do |manager|
|
12
|
+
manager.default_strategies :yauth_users
|
13
|
+
manager.failure_app = Yauth::FailureApp.new
|
14
|
+
end
|
15
|
+
|
16
|
+
get '/' do
|
17
|
+
# the configured user is "admin:admin"
|
18
|
+
request.env['warden'].authenticate!
|
19
|
+
"hello world\n"
|
20
|
+
end
|
data/lib/yauth.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'digest/sha1'
|
3
|
+
require 'thor'
|
4
|
+
require 'warden'
|
5
|
+
|
6
|
+
module Yauth
|
7
|
+
class << self
|
8
|
+
attr_accessor :location
|
9
|
+
end
|
10
|
+
Yauth.location = "config/users.yml"
|
11
|
+
end
|
12
|
+
|
13
|
+
require File.join(File.dirname(__FILE__), "yauth", "user")
|
14
|
+
require File.join(File.dirname(__FILE__), "yauth", "user_manager")
|
15
|
+
require File.join(File.dirname(__FILE__), "yauth", "cli")
|
16
|
+
require File.join(File.dirname(__FILE__), "yauth", "strategy")
|
17
|
+
require File.join(File.dirname(__FILE__), "yauth", "failure_app")
|
data/lib/yauth/cli.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
class Yauth::CLI < Thor
|
3
|
+
|
4
|
+
attr_reader :manager
|
5
|
+
|
6
|
+
desc "add USERNAME PASSWORD", "Adds or updates a user"
|
7
|
+
method_options :config => Yauth.location
|
8
|
+
def add(username, password)
|
9
|
+
init_manager
|
10
|
+
user = Yauth::User.new
|
11
|
+
user.username = username
|
12
|
+
user.plain_password = password
|
13
|
+
manager.add(user)
|
14
|
+
save_manager
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "rm USERNAME", "Remove a user"
|
18
|
+
method_options :config => Yauth.location
|
19
|
+
def rm(username)
|
20
|
+
init_manager
|
21
|
+
manager.remove(username)
|
22
|
+
save_manager
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def init_manager
|
27
|
+
@manager = Yauth::UserManager.load(options[:config])
|
28
|
+
end
|
29
|
+
|
30
|
+
def save_manager
|
31
|
+
FileUtils.mkdir_p(File.dirname(options[:config]))
|
32
|
+
@manager.save(options[:config])
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
class Yauth::FailureApp
|
3
|
+
attr_accessor :realm
|
4
|
+
|
5
|
+
def initialize(realm="Yauth secured area")
|
6
|
+
self.realm = realm
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
[401,
|
11
|
+
{ "Content-Type" => "text/plain",
|
12
|
+
"WWW-Authenticate" => %(Basic realm="#{realm}") },
|
13
|
+
["#{env['warden'].message}\n"]]
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
class Yauth::Strategy < Warden::Strategies::Base
|
3
|
+
|
4
|
+
attr_reader :manager
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
super(*args)
|
8
|
+
@manager = Yauth::UserManager.load(Yauth.location)
|
9
|
+
end
|
10
|
+
|
11
|
+
def authenticate!
|
12
|
+
auth = Rack::Auth::Basic::Request.new(env)
|
13
|
+
credentials = auth.provided? && auth.basic? && auth.credentials
|
14
|
+
if not credentials or not user = manager.authenticate(*credentials)
|
15
|
+
fail!("Could not log in")
|
16
|
+
else
|
17
|
+
success!(user)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.install!
|
22
|
+
Warden::Strategies.add(:yauth_users, self)
|
23
|
+
end
|
24
|
+
end
|
data/lib/yauth/user.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
class Yauth::User
|
3
|
+
attr_accessor :username, :password
|
4
|
+
attr_reader :plain_password
|
5
|
+
|
6
|
+
def initialize(hash={})
|
7
|
+
hash = hash[:user] if hash[:user]
|
8
|
+
hash = hash["user"] if hash["user"]
|
9
|
+
|
10
|
+
self.username = hash[:username] || hash["username"]
|
11
|
+
self.password = hash[:password] || hash["password"]
|
12
|
+
end
|
13
|
+
|
14
|
+
def plain_password=(plain_password)
|
15
|
+
self.password = Digest::SHA256.hexdigest(plain_password)
|
16
|
+
@plain_password = plain_password
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_hash
|
20
|
+
{ "user" => { "username" => username, "password" => password } }
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_yaml(opts={})
|
24
|
+
to_hash.to_yaml(opts)
|
25
|
+
end
|
26
|
+
|
27
|
+
def authenticate(password)
|
28
|
+
return false if password.to_s == ""
|
29
|
+
Digest::SHA256.hexdigest(password) == self.password
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
class Yauth::UserManager
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@list = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def add(user)
|
10
|
+
remove(user.username)
|
11
|
+
@list << user
|
12
|
+
end
|
13
|
+
|
14
|
+
def remove(name)
|
15
|
+
@list.delete(find_by_username(name))
|
16
|
+
end
|
17
|
+
|
18
|
+
def each(&block)
|
19
|
+
@list.each(&block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def save(path)
|
23
|
+
open(path, "w") do |io|
|
24
|
+
io << @list.to_yaml
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def find_by_username(name)
|
29
|
+
@list.find { |u| u.username == name }
|
30
|
+
end
|
31
|
+
|
32
|
+
def authenticate(username, password)
|
33
|
+
user = find_by_username(username)
|
34
|
+
user if user and user.authenticate(password)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.load(path)
|
38
|
+
manager = self.new
|
39
|
+
return manager unless File.exists? path
|
40
|
+
open(path) do |io|
|
41
|
+
YAML.load(io).each { |h| manager.add(Yauth::User.new(h)) }
|
42
|
+
end
|
43
|
+
manager
|
44
|
+
end
|
45
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'yauth'
|
5
|
+
require 'stringio'
|
6
|
+
|
7
|
+
# Requires supporting files with custom matchers and macros, etc,
|
8
|
+
# in ./support/ and its subdirectories.
|
9
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
10
|
+
|
11
|
+
include Yauth
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "spec_helper")
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
describe CLI do
|
5
|
+
|
6
|
+
subject { CLI.new }
|
7
|
+
|
8
|
+
before :each do
|
9
|
+
FileUtils.stub!(:mkdir_p)
|
10
|
+
@options = {}
|
11
|
+
subject.stub!(:options).and_return(@options)
|
12
|
+
end
|
13
|
+
|
14
|
+
[:add, :rm].each do |m|
|
15
|
+
it { should respond_to(m) }
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should add a user to the manager with the specified config" do
|
19
|
+
@options[:config] = "config/users.yml"
|
20
|
+
manager = mock "Manager"
|
21
|
+
UserManager.should_receive(:load).with("config/users.yml").and_return(manager)
|
22
|
+
|
23
|
+
user = mock "User"
|
24
|
+
User.should_receive(:new).and_return(user)
|
25
|
+
user.should_receive(:username=).with("bar")
|
26
|
+
user.should_receive(:plain_password=).with("foo")
|
27
|
+
|
28
|
+
manager.should_receive(:add).with(user)
|
29
|
+
|
30
|
+
FileUtils.should_receive(:mkdir_p).with("config")
|
31
|
+
manager.should_receive(:save).with("config/users.yml")
|
32
|
+
subject.add("bar", "foo")
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should remove from the manager with the specified config" do
|
36
|
+
@options[:config] = "config/users.yml"
|
37
|
+
manager = mock "Manager"
|
38
|
+
UserManager.should_receive(:load).with("config/users.yml").and_return(manager)
|
39
|
+
|
40
|
+
manager.should_receive(:remove).with("bar")
|
41
|
+
|
42
|
+
FileUtils.should_receive(:mkdir_p).with("config")
|
43
|
+
manager.should_receive(:save).with("config/users.yml")
|
44
|
+
subject.rm("bar")
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "spec_helper")
|
2
|
+
|
3
|
+
describe FailureApp do
|
4
|
+
|
5
|
+
subject { FailureApp.new }
|
6
|
+
|
7
|
+
def mock_env(message="hello")
|
8
|
+
warden = mock "warden"
|
9
|
+
warden.stub!(:message).and_return(message)
|
10
|
+
{ 'warden' => warden }
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should return 401" do
|
14
|
+
subject.call(mock_env)[0].should == 401
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should have Content-Type 'text/plain'" do
|
18
|
+
subject.call(mock_env)[1]["Content-Type"].should == 'text/plain'
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should have WWW-Authenticate set" do
|
22
|
+
subject.call(mock_env)[1]["WWW-Authenticate"].should == %(Basic realm="Yauth secured area")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should have the body set as the warden message" do
|
26
|
+
subject.call(mock_env("hello world"))[2][0].should =~ /hello world/
|
27
|
+
end
|
28
|
+
|
29
|
+
it { should respond_to(:realm) }
|
30
|
+
|
31
|
+
it { should respond_to(:realm=) }
|
32
|
+
|
33
|
+
it "should have a default realm" do
|
34
|
+
subject.realm.should == "Yauth secured area"
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should have WWW-Authenticate with the realm configured" do
|
38
|
+
subject.realm = "My Realm"
|
39
|
+
subject.call(mock_env)[1]["WWW-Authenticate"].should == %(Basic realm="My Realm")
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "spec_helper")
|
2
|
+
require 'rack'
|
3
|
+
|
4
|
+
describe Strategy do
|
5
|
+
subject { Strategy.new({}) }
|
6
|
+
|
7
|
+
it { subject.should respond_to(:authenticate!) }
|
8
|
+
|
9
|
+
it "should loads a manager" do
|
10
|
+
manager = mock "Manager"
|
11
|
+
UserManager.should_receive(:load).with(Yauth.location).and_return(manager)
|
12
|
+
subject #needed to initialize the subject
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "with a loaded manager" do
|
16
|
+
|
17
|
+
before(:each) do
|
18
|
+
@manager = mock "Manager"
|
19
|
+
UserManager.stub(:load).and_return(@manager)
|
20
|
+
|
21
|
+
env = mock "Env"
|
22
|
+
subject.should_receive(:env).and_return(env)
|
23
|
+
@request = mock "Request"
|
24
|
+
Rack::Auth::Basic::Request.should_receive(:new).with(env).and_return(@request)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should authenticate if valid credentials are passed" do
|
28
|
+
@request.should_receive(:provided?).and_return(true)
|
29
|
+
@request.should_receive(:basic?).and_return(true)
|
30
|
+
credentials = [mock("credentials1"), mock("credentials2")]
|
31
|
+
@request.should_receive(:credentials).and_return(credentials)
|
32
|
+
|
33
|
+
user = mock "user"
|
34
|
+
@manager.should_receive(:authenticate).with(*credentials).and_return(user)
|
35
|
+
subject.should_receive(:success!).with(user)
|
36
|
+
subject.authenticate!
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should not authenticate if there are no credentials" do
|
40
|
+
@request.should_receive(:provided?).and_return(false)
|
41
|
+
|
42
|
+
subject.should_receive(:fail!).with("Could not log in")
|
43
|
+
subject.should_not_receive(:success!)
|
44
|
+
subject.authenticate!
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe Strategy, "as a class" do
|
50
|
+
it "should install itself into Warden" do
|
51
|
+
Warden::Strategies.should_receive(:add).with(:yauth_users, Strategy)
|
52
|
+
Strategy.install!
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "spec_helper")
|
2
|
+
|
3
|
+
describe UserManager do
|
4
|
+
subject { UserManager.new }
|
5
|
+
|
6
|
+
[:add, :remove, :each, :save].each do |m|
|
7
|
+
it do
|
8
|
+
should respond_to(m)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def mock_user(name)
|
13
|
+
user = mock "user #{name}"
|
14
|
+
user.stub(:username).and_return(name)
|
15
|
+
user
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should add a user" do
|
19
|
+
user = mock_user "user"
|
20
|
+
subject.add user
|
21
|
+
subject.to_a.should == [user]
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should not add the same user twice" do
|
25
|
+
user = mock_user "user"
|
26
|
+
subject.add user
|
27
|
+
subject.add user
|
28
|
+
subject.to_a.should == [user]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should remove a user given its username" do
|
32
|
+
user = mock_user "name"
|
33
|
+
subject.add user
|
34
|
+
subject.remove("name").should == user
|
35
|
+
subject.to_a.should == []
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should yield each added user" do
|
39
|
+
first = mock_user "first"
|
40
|
+
second = mock_user "second"
|
41
|
+
subject.should_receive(:each).and_yield(first, second)
|
42
|
+
subject.add first
|
43
|
+
subject.add second
|
44
|
+
subject.each {}
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should save all its user to the specified file" do
|
48
|
+
first = User.new(:username => "first", :password => "123")
|
49
|
+
second = User.new(:username => "second", :password => "456")
|
50
|
+
subject.add(first)
|
51
|
+
subject.add(second)
|
52
|
+
|
53
|
+
path = "a/path/to.yml"
|
54
|
+
io = StringIO.new
|
55
|
+
subject.should_receive(:open).with(path, "w").and_yield(io)
|
56
|
+
subject.save(path)
|
57
|
+
io.string.should == <<-EOF
|
58
|
+
---
|
59
|
+
- user:
|
60
|
+
username: first
|
61
|
+
password: "123"
|
62
|
+
- user:
|
63
|
+
username: second
|
64
|
+
password: "456"
|
65
|
+
EOF
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should find a user by its username" do
|
69
|
+
first = User.new(:username => "first", :password => "123")
|
70
|
+
second = User.new(:username => "second", :password => "456")
|
71
|
+
subject.add(first)
|
72
|
+
subject.add(second)
|
73
|
+
|
74
|
+
subject.find_by_username("first").should == first
|
75
|
+
subject.find_by_username("second").should == second
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should authenticate a user by its username and password" do
|
79
|
+
user = mock "user"
|
80
|
+
subject.should_receive(:find_by_username).with("name").and_return(user)
|
81
|
+
user.should_receive(:authenticate).with("password").and_return(true)
|
82
|
+
|
83
|
+
subject.authenticate("name", "password").should == user
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should not authenticate a user if the password doesn't match" do
|
87
|
+
user = mock "user"
|
88
|
+
subject.should_receive(:find_by_username).with("name").and_return(user)
|
89
|
+
user.should_receive(:authenticate).with("password").and_return(false)
|
90
|
+
|
91
|
+
subject.authenticate("name", "password").should be_false
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should not authenticate if there is no match" do
|
95
|
+
subject.should_receive(:find_by_username).with("name").and_return(nil)
|
96
|
+
subject.authenticate("name", "password").should be_false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe UserManager, "as a class" do
|
101
|
+
|
102
|
+
subject { UserManager }
|
103
|
+
|
104
|
+
it "should mix in Enumerable" do
|
105
|
+
subject.ancestors.should include(Enumerable)
|
106
|
+
end
|
107
|
+
|
108
|
+
it { should respond_to(:load) }
|
109
|
+
|
110
|
+
describe "#load" do
|
111
|
+
it "should load from a yaml file if that file exists" do
|
112
|
+
path = "a/path/to.yml"
|
113
|
+
io = StringIO.new <<-EOF
|
114
|
+
- user:
|
115
|
+
username: first
|
116
|
+
password: 123456
|
117
|
+
- user:
|
118
|
+
username: second
|
119
|
+
password: 789012
|
120
|
+
EOF
|
121
|
+
File.stub(:exists?).with(path).and_return(true)
|
122
|
+
UserManager.should_receive(:open).with(path).and_yield(io)
|
123
|
+
manager = UserManager.load(path)
|
124
|
+
|
125
|
+
ary = manager.to_a
|
126
|
+
ary.size.should == 2
|
127
|
+
|
128
|
+
ary[0].username.should == "first"
|
129
|
+
ary[0].password.should == 123456
|
130
|
+
ary[1].username.should == "second"
|
131
|
+
ary[1].password.should == 789012
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should create a new UserManager if the file doesn't exists" do
|
135
|
+
path = "a/path/to.yml"
|
136
|
+
File.stub(:exists?).with(path).and_return(false)
|
137
|
+
manager = UserManager.load(path)
|
138
|
+
manager.to_a.should == []
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "spec_helper")
|
2
|
+
|
3
|
+
describe User do
|
4
|
+
subject { User.new }
|
5
|
+
|
6
|
+
[:username, :password, :plain_password].each do |attr|
|
7
|
+
it "should have an #{attr} accessor" do
|
8
|
+
should respond_to(attr)
|
9
|
+
should respond_to(attr.to_s + "=")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should set the real password based on the plain password" do
|
14
|
+
password = "hello world"
|
15
|
+
hash = Digest::SHA256.hexdigest(password)
|
16
|
+
subject.plain_password = password
|
17
|
+
subject.password.should == hash
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should memorize the plain password until the end of the session" do
|
21
|
+
password = "hello world"
|
22
|
+
subject.plain_password = password
|
23
|
+
subject.plain_password.should == password
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should be transformed to an hash" do
|
27
|
+
subject.username = "XYZ"
|
28
|
+
subject.password = "ABC"
|
29
|
+
subject.to_hash.should == { "user" => { "username" => "XYZ", "password" => "ABC" } }
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should yaml nicely" do
|
33
|
+
subject.username = "XYZ"
|
34
|
+
subject.password = "ABC"
|
35
|
+
subject.to_yaml.should ==<<-EOF
|
36
|
+
---
|
37
|
+
user:
|
38
|
+
username: XYZ
|
39
|
+
password: ABC
|
40
|
+
EOF
|
41
|
+
end
|
42
|
+
|
43
|
+
it { should respond_to(:authenticate) }
|
44
|
+
|
45
|
+
it "should authenticate with the right password" do
|
46
|
+
subject.username = "XYZ"
|
47
|
+
subject.plain_password = "ABC"
|
48
|
+
subject.authenticate("ABC").should be_true
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should not authenticate with the wrong password" do
|
52
|
+
subject.username = "XYZ"
|
53
|
+
subject.plain_password = "ABC"
|
54
|
+
subject.authenticate("XYZ").should be_false
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should not authenticate with the empty password" do
|
58
|
+
subject.username = "XYZ"
|
59
|
+
subject.plain_password = "ABC"
|
60
|
+
subject.authenticate("").should be_false
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should not authenticate with the nil password" do
|
64
|
+
subject.username = "XYZ"
|
65
|
+
subject.plain_password = "ABC"
|
66
|
+
subject.authenticate(nil).should be_false
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "as a class" do
|
70
|
+
|
71
|
+
it "should build a user from a prefixed hash with strings" do
|
72
|
+
u = User.new({ "user" => { "username" => "XYZ", "password" => "ABC" } })
|
73
|
+
u.username.should == "XYZ"
|
74
|
+
u.password.should == "ABC"
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should build a user from an prefixed hash with symbols" do
|
78
|
+
u = User.new({ :"user" => { :"username" => "XYZ", :"password" => "ABC" } })
|
79
|
+
u.username.should == "XYZ"
|
80
|
+
u.password.should == "ABC"
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should build a user from a hash with strings" do
|
84
|
+
u = User.new({ "username" => "XYZ", "password" => "ABC" })
|
85
|
+
u.username.should == "XYZ"
|
86
|
+
u.password.should == "ABC"
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should build a user from an hash with symbols" do
|
90
|
+
u = User.new({ :"username" => "XYZ", :"password" => "ABC" })
|
91
|
+
u.username.should == "XYZ"
|
92
|
+
u.password.should == "ABC"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/spec/yauth_spec.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe Yauth do
|
4
|
+
subject { Yauth }
|
5
|
+
|
6
|
+
it { should respond_to(:location) }
|
7
|
+
it { should respond_to(:location=) }
|
8
|
+
|
9
|
+
it "should store the default yml location" do
|
10
|
+
subject.location.should == "config/users.yml"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should be possible to overwrite the default yml location" do
|
14
|
+
subject.location = "users.yml"
|
15
|
+
subject.location.should == "users.yml"
|
16
|
+
end
|
17
|
+
end
|
data/yauth.gemspec
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{yauth}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Matteo Collina"]
|
12
|
+
s.date = %q{2011-03-01}
|
13
|
+
s.default_executable = %q{yauth}
|
14
|
+
s.description = %q{Yauth is a extremely simple authentication solution for prototipes, developed as a warden strategy. It uses a yaml file to store usernames and hashed password. It provides a better-than-nothing security.}
|
15
|
+
s.email = %q{matteo@matteocollina.com}
|
16
|
+
s.executables = ["yauth"]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"LICENSE.txt",
|
19
|
+
"README.rdoc"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".autotest",
|
23
|
+
".document",
|
24
|
+
".rspec",
|
25
|
+
".rvmrc",
|
26
|
+
"LICENSE.txt",
|
27
|
+
"README.rdoc",
|
28
|
+
"Rakefile",
|
29
|
+
"VERSION",
|
30
|
+
"bin/yauth",
|
31
|
+
"examples/sinatra/app.rb",
|
32
|
+
"examples/sinatra/config/users.yml",
|
33
|
+
"lib/yauth.rb",
|
34
|
+
"lib/yauth/cli.rb",
|
35
|
+
"lib/yauth/failure_app.rb",
|
36
|
+
"lib/yauth/strategy.rb",
|
37
|
+
"lib/yauth/user.rb",
|
38
|
+
"lib/yauth/user_manager.rb",
|
39
|
+
"spec/spec_helper.rb",
|
40
|
+
"spec/yauth/cli_spec.rb",
|
41
|
+
"spec/yauth/failure_app_spec.rb",
|
42
|
+
"spec/yauth/strategy_spec.rb",
|
43
|
+
"spec/yauth/user_manager_spec.rb",
|
44
|
+
"spec/yauth/user_spec.rb",
|
45
|
+
"spec/yauth_spec.rb",
|
46
|
+
"yauth.gemspec"
|
47
|
+
]
|
48
|
+
s.homepage = %q{http://github.com/mcollina/yauth}
|
49
|
+
s.licenses = ["MIT"]
|
50
|
+
s.require_paths = ["lib"]
|
51
|
+
s.rubygems_version = %q{1.5.2}
|
52
|
+
s.summary = %q{A drop-in authentication solution for prototypes.}
|
53
|
+
s.test_files = [
|
54
|
+
"examples/sinatra/app.rb",
|
55
|
+
"spec/spec_helper.rb",
|
56
|
+
"spec/yauth/cli_spec.rb",
|
57
|
+
"spec/yauth/failure_app_spec.rb",
|
58
|
+
"spec/yauth/strategy_spec.rb",
|
59
|
+
"spec/yauth/user_manager_spec.rb",
|
60
|
+
"spec/yauth/user_spec.rb",
|
61
|
+
"spec/yauth_spec.rb"
|
62
|
+
]
|
63
|
+
|
64
|
+
if s.respond_to? :specification_version then
|
65
|
+
s.specification_version = 3
|
66
|
+
|
67
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
68
|
+
s.add_runtime_dependency(%q<warden>, ["~> 1.0"])
|
69
|
+
s.add_runtime_dependency(%q<thor>, ["~> 0.14.0"])
|
70
|
+
s.add_development_dependency(%q<test_notifier>, ["~> 0.3.6"])
|
71
|
+
s.add_development_dependency(%q<autotest>, ["~> 4.4"])
|
72
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
73
|
+
else
|
74
|
+
s.add_dependency(%q<warden>, ["~> 1.0"])
|
75
|
+
s.add_dependency(%q<thor>, ["~> 0.14.0"])
|
76
|
+
s.add_dependency(%q<test_notifier>, ["~> 0.3.6"])
|
77
|
+
s.add_dependency(%q<autotest>, ["~> 4.4"])
|
78
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
79
|
+
end
|
80
|
+
else
|
81
|
+
s.add_dependency(%q<warden>, ["~> 1.0"])
|
82
|
+
s.add_dependency(%q<thor>, ["~> 0.14.0"])
|
83
|
+
s.add_dependency(%q<test_notifier>, ["~> 0.3.6"])
|
84
|
+
s.add_dependency(%q<autotest>, ["~> 4.4"])
|
85
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
metadata
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yauth
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Matteo Collina
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-03-01 00:00:00 +01:00
|
19
|
+
default_executable: yauth
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: warden
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 15
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
version: "1.0"
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: thor
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 39
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
- 14
|
48
|
+
- 0
|
49
|
+
version: 0.14.0
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: test_notifier
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 31
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
- 3
|
64
|
+
- 6
|
65
|
+
version: 0.3.6
|
66
|
+
type: :development
|
67
|
+
version_requirements: *id003
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: autotest
|
70
|
+
prerelease: false
|
71
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 19
|
77
|
+
segments:
|
78
|
+
- 4
|
79
|
+
- 4
|
80
|
+
version: "4.4"
|
81
|
+
type: :development
|
82
|
+
version_requirements: *id004
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rcov
|
85
|
+
prerelease: false
|
86
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
hash: 3
|
92
|
+
segments:
|
93
|
+
- 0
|
94
|
+
version: "0"
|
95
|
+
type: :development
|
96
|
+
version_requirements: *id005
|
97
|
+
description: Yauth is a extremely simple authentication solution for prototipes, developed as a warden strategy. It uses a yaml file to store usernames and hashed password. It provides a better-than-nothing security.
|
98
|
+
email: matteo@matteocollina.com
|
99
|
+
executables:
|
100
|
+
- yauth
|
101
|
+
extensions: []
|
102
|
+
|
103
|
+
extra_rdoc_files:
|
104
|
+
- LICENSE.txt
|
105
|
+
- README.rdoc
|
106
|
+
files:
|
107
|
+
- .autotest
|
108
|
+
- .document
|
109
|
+
- .rspec
|
110
|
+
- .rvmrc
|
111
|
+
- LICENSE.txt
|
112
|
+
- README.rdoc
|
113
|
+
- Rakefile
|
114
|
+
- VERSION
|
115
|
+
- bin/yauth
|
116
|
+
- examples/sinatra/app.rb
|
117
|
+
- examples/sinatra/config/users.yml
|
118
|
+
- lib/yauth.rb
|
119
|
+
- lib/yauth/cli.rb
|
120
|
+
- lib/yauth/failure_app.rb
|
121
|
+
- lib/yauth/strategy.rb
|
122
|
+
- lib/yauth/user.rb
|
123
|
+
- lib/yauth/user_manager.rb
|
124
|
+
- spec/spec_helper.rb
|
125
|
+
- spec/yauth/cli_spec.rb
|
126
|
+
- spec/yauth/failure_app_spec.rb
|
127
|
+
- spec/yauth/strategy_spec.rb
|
128
|
+
- spec/yauth/user_manager_spec.rb
|
129
|
+
- spec/yauth/user_spec.rb
|
130
|
+
- spec/yauth_spec.rb
|
131
|
+
- yauth.gemspec
|
132
|
+
has_rdoc: true
|
133
|
+
homepage: http://github.com/mcollina/yauth
|
134
|
+
licenses:
|
135
|
+
- MIT
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
|
139
|
+
require_paths:
|
140
|
+
- lib
|
141
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
142
|
+
none: false
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
hash: 3
|
147
|
+
segments:
|
148
|
+
- 0
|
149
|
+
version: "0"
|
150
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
|
+
none: false
|
152
|
+
requirements:
|
153
|
+
- - ">="
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
hash: 3
|
156
|
+
segments:
|
157
|
+
- 0
|
158
|
+
version: "0"
|
159
|
+
requirements: []
|
160
|
+
|
161
|
+
rubyforge_project:
|
162
|
+
rubygems_version: 1.5.2
|
163
|
+
signing_key:
|
164
|
+
specification_version: 3
|
165
|
+
summary: A drop-in authentication solution for prototypes.
|
166
|
+
test_files:
|
167
|
+
- examples/sinatra/app.rb
|
168
|
+
- spec/spec_helper.rb
|
169
|
+
- spec/yauth/cli_spec.rb
|
170
|
+
- spec/yauth/failure_app_spec.rb
|
171
|
+
- spec/yauth/strategy_spec.rb
|
172
|
+
- spec/yauth/user_manager_spec.rb
|
173
|
+
- spec/yauth/user_spec.rb
|
174
|
+
- spec/yauth_spec.rb
|