yauth 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ require 'test_notifier/runner/autotest'
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm --create use ruby-1.8.7-p302@yauth
@@ -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.
@@ -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
+
@@ -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
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems" # ruby1.9 doesn't "require" it though
3
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
4
+ require "yauth"
5
+ Yauth::CLI.start
@@ -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
@@ -0,0 +1,4 @@
1
+ ---
2
+ - user:
3
+ username: admin
4
+ password: 8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918
@@ -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")
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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