authentication-service 0.0.1.a1

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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.rvmrc ADDED
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
+ # development environment upon cd'ing into the directory
5
+
6
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
7
+ # Only full ruby name is supported here, for short names use:
8
+ # echo "rvm use 1.9.3" > .rvmrc
9
+ environment_id="ruby-1.9.3-p0@auth-svc"
10
+
11
+ #
12
+ # Uncomment the following lines if you want to verify rvm version per project
13
+ #
14
+ # rvmrc_rvm_version="1.10.2" # 1.10.1 seams as a safe start
15
+ # eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
16
+ # echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
17
+ # return 1
18
+ # }
19
+ #
20
+
21
+ #
22
+ # Uncomment following line if you want options to be set only for given project.
23
+ #
24
+ # PROJECT_JRUBY_OPTS=( --1.9 )
25
+ #
26
+ # The variable PROJECT_JRUBY_OPTS requires the following to be run in shell:
27
+ #
28
+ # chmod +x ${rvm_path}/hooks/after_use_jruby_opts
29
+ #
30
+
31
+ #
32
+ # First we attempt to load the desired environment directly from the environment
33
+ # file. This is very fast and efficient compared to running through the entire
34
+ # CLI and selector. If you want feedback on which environment was used then
35
+ # insert the word 'use' after --create as this triggers verbose mode.
36
+ #
37
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
38
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
39
+ then
40
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
41
+
42
+ if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]
43
+ then
44
+ . "${rvm_path:-$HOME/.rvm}/hooks/after_use"
45
+ fi
46
+ else
47
+ # If the environment file has not yet been created, use the RVM CLI to select.
48
+ if ! rvm --create "$environment_id"
49
+ then
50
+ echo "Failed to create RVM environment '${environment_id}'."
51
+ return 1
52
+ fi
53
+ fi
54
+
55
+ #
56
+ # If you use an RVM gemset file to install a list of gems (*.gems), you can have
57
+ # it be automatically loaded. Uncomment the following and adjust the filename if
58
+ # necessary.
59
+ #
60
+ # filename=".gems"
61
+ # if [[ -s "$filename" ]]
62
+ # then
63
+ # rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
64
+ # fi
65
+
66
+ # If you use bundler, this might be useful to you:
67
+ # if [[ -s Gemfile ]] && ! command -v bundle >/dev/null
68
+ # then
69
+ # printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
70
+ # gem install bundler
71
+ # fi
72
+ # if [[ -s Gemfile ]] && command -v bundle
73
+ # then
74
+ # bundle install
75
+ # fi
76
+
77
+ if [[ $- == *i* ]] # check for interactive shells
78
+ then
79
+ echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)" # show the user the ruby and gemset they are using in green
80
+ else
81
+ echo "Using: $GEM_HOME" # don't use colors in interactive shells
82
+ fi
83
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in authentication-service.gemspec
4
+ gemspec
data/README ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "authentication-service/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "authentication-service"
7
+ s.version = AuthenticationService::VERSION
8
+ s.authors = ["Evgeny Myasishchev"]
9
+ s.email = ["evgeny.myasishchev@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Authentication service for Rails (and not only)}
12
+ s.description = %q{Provides authentication related stuff.}
13
+
14
+ s.rubyforge_project = "authentication-service"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "rspec"
22
+ end
@@ -0,0 +1,17 @@
1
+ require 'digest/sha2'
2
+
3
+ class AuthenticationService::Account
4
+ attr_reader :account_id
5
+ attr_accessor :email, :password_hash
6
+
7
+ def initialize(account_id, email, password_hash)
8
+ @account_id, @email, @password_hash = account_id, email, password_hash
9
+ end
10
+
11
+ class << self
12
+
13
+ def hash_for_password(password)
14
+ Digest::SHA512.hexdigest(password)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,41 @@
1
+ class AuthenticationService::Base
2
+ attr_reader :accounts_repository, :sessions_repository
3
+
4
+ def initialize(accounts_repository, sessions_repository)
5
+ @accounts_repository = accounts_repository
6
+ @sessions_repository = sessions_repository
7
+ end
8
+
9
+ def register_account(email, password)
10
+ accounts_repository.create(email, password)
11
+ end
12
+
13
+ #
14
+ # Returns Session instance if it exists and is valid
15
+ #
16
+ def authenticate_by_session_id(session_id, ip_address)
17
+ session = sessions_repository.find_by_session_id(session_id)
18
+ return nil unless session
19
+ return nil unless session.ip_address == ip_address
20
+ sessions_repository.touch session.session_id
21
+ return session
22
+ end
23
+
24
+ #
25
+ # Validates login/password and returns new Session instance
26
+ # Login is email
27
+ #
28
+ def authenticate_by_login(login, password, ip_address)
29
+ account = accounts_repository.find_by_email(login)
30
+ return nil unless account
31
+
32
+ password_hash = AuthenticationService::Account.hash_for_password(password)
33
+ return nil unless account.password_hash == password_hash
34
+
35
+ sessions_repository.create AuthenticationService::Session.create_new(account, ip_address)
36
+ end
37
+
38
+ def sign_out(session)
39
+ sessions_repository.destroy(session)
40
+ end
41
+ end
@@ -0,0 +1,22 @@
1
+ module AuthenticationService::Persistance
2
+ class AccountsRepository
3
+ include AuthenticationService::Persistance::ModelHelpers
4
+
5
+ attr_reader :model_class
6
+
7
+ def initialize(model_class)
8
+ @model_class = model_class
9
+ end
10
+
11
+ def create(email, password)
12
+ rec = @model_class.create! :email => email, :password_hash => AuthenticationService::Account.hash_for_password(password)
13
+ to_account_model rec
14
+ end
15
+
16
+ def find_by_email(email)
17
+ rec = @model_class.find_by_email(email)
18
+ return nil unless rec
19
+ to_account_model rec
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ module AuthenticationService::Persistance
2
+ class SessionsRepository
3
+ include AuthenticationService::Persistance::ModelHelpers
4
+
5
+ attr_reader :model_class
6
+
7
+ def initialize(model_class)
8
+ @model_class = model_class
9
+ end
10
+
11
+ def find_by_session_id(session_id)
12
+ rec = @model_class.includes(:account).find_by_session_id(session_id)
13
+ return nil unless rec
14
+ return to_session_model rec
15
+ end
16
+
17
+ def create(session)
18
+ @model_class.create! :session_id => session.session_id,
19
+ :account_id => session.account.account_id,
20
+ :ip_address => session.ip_address,
21
+ :created_at => session.created_at,
22
+ :updated_at => session.last_time_used_at
23
+ session
24
+ end
25
+
26
+ def touch(session_id)
27
+ rec = @model_class.find_by_session_id(session_id)
28
+ rec.touch
29
+ end
30
+
31
+ def destroy(session)
32
+ rec = @model_class.find_by_session_id(session.session_id)
33
+ rec.destroy
34
+ session
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,19 @@
1
+ module AuthenticationService::Persistance
2
+ autoload :AccountsRepository, 'authentication-service/persistance/accounts-repository'
3
+ autoload :SessionsRepository, 'authentication-service/persistance/sessions-repository'
4
+
5
+
6
+ module ModelHelpers
7
+ def to_session_model(session_record)
8
+ AuthenticationService::Session.new(session_record.session_id,
9
+ to_account_model(session_record.account),
10
+ session_record.ip_address,
11
+ session_record.created_at,
12
+ session_record.updated_at)
13
+ end
14
+
15
+ def to_account_model(account_record)
16
+ AuthenticationService::Account.new account_record.id, account_record.email, account_record.password_hash
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,38 @@
1
+ module AuthenticationService::Rails::SessionsControllerActions
2
+ def self.included(base)
3
+ base.before_filter :authenticate, :only => :destroy
4
+ end
5
+
6
+ def new
7
+ end
8
+
9
+ def create
10
+ authenticated_session = authentication_service.authenticate_by_login(params[:login], params[:password], request.remote_ip)
11
+ if authenticated_session
12
+ session[:authenticated_session_id] = authenticated_session.session_id
13
+ redirect_back
14
+ else
15
+ respond_to do |format|
16
+ format.html {
17
+ render :action => :new, :status => :unauthorized
18
+ }
19
+ format.json {
20
+ render :nothing => true, :status => :unauthorized
21
+ }
22
+ end
23
+ end
24
+ end
25
+
26
+ def destroy
27
+ authentication_service.sign_out(current_session)
28
+ reset_session
29
+ respond_to do |format|
30
+ format.html {
31
+ redirect_to(sign_in_url)
32
+ }
33
+ format.json {
34
+ render :json => {'navigate-to' => sign_in_url}
35
+ }
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,89 @@
1
+ module AuthenticationService::Rails
2
+
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ class OptionsStore
9
+ class << self
10
+ attr_accessor :account_class, :session_class
11
+ end
12
+ end
13
+
14
+ def account_class
15
+ OptionsStore.account_class
16
+ end
17
+
18
+ def session_class
19
+ OptionsStore.session_class
20
+ end
21
+
22
+ def authentication_service(options)
23
+ options = {
24
+ :account => nil,
25
+ :session => nil
26
+ }.merge(options)
27
+
28
+ raise "Account persistance model class not assigned" unless options[:account]
29
+ raise "Session persistance model class not assigned" unless options[:session]
30
+
31
+ OptionsStore.account_class = options[:account]
32
+ OptionsStore.session_class = options[:session]
33
+ end
34
+
35
+ def behave_as_sessins_controller
36
+ self.send(:include, AuthenticationService::Rails::SessionsControllerActions)
37
+ end
38
+ end
39
+
40
+ autoload :SessionsControllerActions, 'authentication-service/rails/sessions-controller-actions'
41
+
42
+ attr_accessor :current_session
43
+
44
+ def authenticated?
45
+ !current_session.blank?
46
+ end
47
+
48
+ def authenticate
49
+ authenticate_from_session if session[:authenticated_session_id]
50
+ redirect_not_authenticated unless authenticated?
51
+ end
52
+
53
+ def authentication_service
54
+ @authentication_service ||= begin
55
+ raise "Authentication service not configured. Please use authentication_service to configure it." unless self.class.account_class
56
+ raise "Authentication service not configured. Please use authentication_service to configure it." unless self.class.session_class
57
+ accounts_repository = AuthenticationService::Persistance::AccountsRepository.new(self.class.account_class)
58
+ sessions_repository = AuthenticationService::Persistance::SessionsRepository.new(self.class.session_class)
59
+ AuthenticationService::Base.new(accounts_repository, sessions_repository)
60
+ end
61
+ end
62
+
63
+ def authentication_service=(value)
64
+ @authentication_service = value
65
+ end
66
+
67
+ protected
68
+ def redirect_not_authenticated
69
+ session[:return_to] = request.fullpath
70
+ redirect_to sign_in_url
71
+ end
72
+
73
+ def redirect_back
74
+ respond_to do |format|
75
+ format.html {
76
+ redirect_to(session[:return_to] || root_url)
77
+ }
78
+ format.json {
79
+ render :json => {'navigate-to' => (session[:return_to] || root_url)}
80
+ }
81
+ end
82
+ session[:return_to] = nil
83
+ end
84
+
85
+ private
86
+ def authenticate_from_session
87
+ self.current_session = authentication_service.authenticate_by_session_id(session[:authenticated_session_id], request.remote_ip)
88
+ end
89
+ end
@@ -0,0 +1,32 @@
1
+ require 'securerandom'
2
+
3
+ class AuthenticationService::Session
4
+ attr_reader :session_id
5
+
6
+ #Account instance of this session
7
+ attr_reader :account
8
+
9
+ #Ip address of the client that has issued the session
10
+ attr_reader :ip_address
11
+
12
+ #Date/Time when session has been started
13
+ attr_reader :created_at
14
+
15
+ #Date/Time when the session has been used last time
16
+ attr_reader :last_time_used_at
17
+
18
+ def initialize(session_id, account, ip_address, created_at, last_time_used_at)
19
+ @session_id, @account, @ip_address, @created_at, @last_time_used_at = session_id, account, ip_address, created_at, last_time_used_at
20
+ end
21
+
22
+ # Make sure the session has not expired and ip_address is the same as originally issued
23
+ def is_valid?(ip_address)
24
+
25
+ end
26
+
27
+ class << self
28
+ def create_new(account, ip_address)
29
+ new(SecureRandom.hex(32), account, ip_address, DateTime.now, DateTime.now)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module AuthenticationService
2
+ VERSION = "0.0.1.a1"
3
+ end
@@ -0,0 +1,9 @@
1
+ require "authentication-service/version"
2
+
3
+ module AuthenticationService
4
+ autoload :Account, 'authentication-service/account'
5
+ autoload :Base, 'authentication-service/base'
6
+ autoload :Persistance, 'authentication-service/persistance'
7
+ autoload :Rails, 'authentication-service/rails'
8
+ autoload :Session, 'authentication-service/session'
9
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe Account do
4
+ describe "hash_for_password" do
5
+ it "should use SHA512 as hashing method" do
6
+ sha512_hash = Digest::SHA512.hexdigest('some-password')
7
+ Account.hash_for_password('some-password').should eql sha512_hash
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+
3
+ describe AuthenticationService::Base do
4
+ before(:each) do
5
+ @sessions_repository = mock(:sessions_repository)
6
+ @accounts_repository = mock(:accounts_repository)
7
+ @authentication_service = AuthenticationService::Base.new(@accounts_repository, @sessions_repository)
8
+
9
+ @account = Account.new(10, "mail@domain.com", "some-password-hash")
10
+ @session = Session.new("session-id", @account, "127.0.0.1", nil, nil)
11
+ end
12
+
13
+ describe "register_account" do
14
+ it "should use accounts repository to create new account" do
15
+ account = Account.new(10, "mail@domain.com", "some-password-hash")
16
+ @accounts_repository.should_receive(:create).with("mail@domain.com", "some-password").and_return(account)
17
+
18
+ @authentication_service.register_account "mail@domain.com", "some-password"
19
+ end
20
+
21
+ it "should return created account instance" do
22
+ account = Account.new(10, "mail@domain.com", "some-password-hash")
23
+ @accounts_repository.stub(:create).and_return(account)
24
+
25
+ @authentication_service.register_account("mail@domain.com", "some-password").should be account
26
+ end
27
+ end
28
+
29
+ describe "authenticate_by_session_id" do
30
+ before(:each) do
31
+ @sessions_repository.stub(:find_by_session_id) { @session }
32
+ @sessions_repository.stub(:touch)
33
+ end
34
+
35
+ it "should use sessions repository to find stored sessions" do
36
+ @sessions_repository.should_receive(:find_by_session_id).with("session-id").and_return(@session)
37
+ @authentication_service.authenticate_by_session_id("session-id", "127.0.0.1")
38
+ end
39
+
40
+ it "should return nil if session does not exist" do
41
+ @sessions_repository.stub(:find_by_session_id) { nil }
42
+ @authentication_service.authenticate_by_session_id("session-id", "127.0.0.1").should be_nil
43
+ end
44
+
45
+ it "should return the session if ip_address is the same as initial" do
46
+ @authentication_service.authenticate_by_session_id("session-id", "127.0.0.1").should be @session
47
+ end
48
+
49
+ it "should return nil if ip_address is different from initial" do
50
+ @authentication_service.authenticate_by_session_id("session-id", "127.0.0.2").should be_nil
51
+ end
52
+
53
+ it "should touch session so last_time_used_at gets updated" do
54
+ @sessions_repository.should_receive(:touch).with("session-id")
55
+ @authentication_service.authenticate_by_session_id("session-id", "127.0.0.1")
56
+ end
57
+ end
58
+
59
+ describe "authenticate_by_login" do
60
+ before(:each) do
61
+ @accounts_repository.stub(:find_by_email) { @account }
62
+ Session.stub(:create_new) { @session }
63
+ @sessions_repository.stub(:create) { @session }
64
+ Account.stub(:hash_for_password) { "some-password-hash" }
65
+ end
66
+
67
+ it "should use accounts repository to obtain accounts" do
68
+ @accounts_repository.should_receive(:find_by_email).with("mail@domain.com").and_return(@account)
69
+ @authentication_service.authenticate_by_login("mail@domain.com", "some-password", "127.0.0.1")
70
+ end
71
+
72
+ describe "account is found and password is valid" do
73
+
74
+ it "should return new session instance" do
75
+ @authentication_service.authenticate_by_login("mail@domain.com", "some-password", "127.0.0.1").should be @session
76
+ end
77
+
78
+ it "should create new session using sessions repository" do
79
+ @sessions_repository.should_receive(:create).with(@session).and_return(@session)
80
+ @authentication_service.authenticate_by_login("mail@domain.com", "some-password", "127.0.0.1")
81
+ end
82
+ end
83
+
84
+ it "should return nil if account not found" do
85
+ @accounts_repository.stub(:find_by_email) { nil }
86
+ @authentication_service.authenticate_by_login("mail@domain.com", "some-password", "127.0.0.1").should be_nil
87
+ end
88
+
89
+ it "should return nil if account is found but password is invalid" do
90
+ Account.stub(:hash_for_password) { "invalid-password-hash" }
91
+ @authentication_service.authenticate_by_login("mail@domain.com", "some-password", "127.0.0.1").should be_nil
92
+ end
93
+ end
94
+
95
+ describe "sign_out" do
96
+ it "should destroy the session using repository" do
97
+ @sessions_repository.should_receive(:destroy).with(@session).and_return(@session)
98
+ @authentication_service.sign_out(@session)
99
+ end
100
+
101
+ it "should return session object" do
102
+ @sessions_repository.stub(:destroy) {@session}
103
+ @authentication_service.sign_out(@session).should be @session
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe AuthenticationService::Persistance::AccountsRepository do
4
+ before(:each) do
5
+ @model_class = mock(:account_model_class)
6
+ @repository = AuthenticationService::Persistance::AccountsRepository.new @model_class
7
+ end
8
+
9
+ describe "create" do
10
+ it "should create corresponding database record" do
11
+ password_hash = AuthenticationService::Account.hash_for_password("some-password")
12
+ @model_class.should_receive(:create!).
13
+ with(:email => "mail@domain.com", :password_hash => password_hash).
14
+ and_return(mock(:account_rec, :id => 999, :email => "mail@domain.com", :password_hash => password_hash))
15
+ @repository.create "mail@domain.com", "some-password"
16
+ end
17
+
18
+ it "return Account instance" do
19
+ password_hash = AuthenticationService::Account.hash_for_password("some-password")
20
+ @model_class.stub(:create!).
21
+ and_return(mock(:account_rec, :id => 999, :email => "mail@domain.com", :password_hash => password_hash))
22
+ account = @repository.create "mail@domain.com", "some-password"
23
+ account.should be_instance_of AuthenticationService::Account
24
+ account.account_id.should eql 999
25
+ account.email.should eql "mail@domain.com"
26
+ end
27
+
28
+ it "should hash password" do
29
+ AuthenticationService::Account.should_receive(:hash_for_password).with("some-password").and_return("hashed-password")
30
+ @model_class.stub(:create!).
31
+ and_return(mock(:account_rec, :id => 999, :email => "mail@domain.com", :password_hash => "hashed-password"))
32
+ account = @repository.create "mail@domain.com", "some-password"
33
+ account.password_hash.should eql "hashed-password"
34
+ end
35
+ end
36
+
37
+ describe "find_by_email" do
38
+ it "should return corresponding database record searching by email" do
39
+ rec = mock :account_rec, :id => 999, :email => "mail@domain.com", :password_hash => "some-hash"
40
+ @model_class.should_receive(:find_by_email).with("mail@domain.com").and_return(rec)
41
+
42
+ account = @repository.find_by_email rec.email
43
+ account.account_id.should eql rec.id
44
+ account.password_hash.should eql rec.password_hash
45
+ end
46
+
47
+ it "should return nil if no such account" do
48
+ @model_class.should_receive(:find_by_email).with("unknown-mail@domain.com").and_return(nil)
49
+ @repository.find_by_email("unknown-mail@domain.com").should be_nil
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe AuthenticationService::Persistance::SessionsRepository do
4
+ include AuthenticationService::Persistance::ModelHelpers
5
+
6
+ before(:each) do
7
+ @model_class = mock(:session_model_class)
8
+ @repository = AuthenticationService::Persistance::SessionsRepository.new(@model_class)
9
+ @account_rec = mock :account_rec, :id => 999, :email => "mail@domain.com", :password_hash => "password-hash"
10
+ @session_rec = mock :session_rec,
11
+ :id => 555,
12
+ :session_id => "unique-session-id",
13
+ :account_id => @account_rec.id,
14
+ :account => @account_rec,
15
+ :ip_address => "127.0.0.2",
16
+ :created_at => DateTime.now,
17
+ :updated_at => DateTime.now
18
+ end
19
+
20
+ describe "find_by_session_id" do
21
+ before(:each) do
22
+ @model_class.stub(:includes) { @model_class }
23
+ @model_class.stub(:find_by_session_id) { @session_rec }
24
+ end
25
+
26
+ it "should return corresponding session instance" do
27
+ @model_class.should_receive(:includes).with(:account) { @model_class }
28
+ @model_class.should_receive(:find_by_session_id).with("unique-session-id") { @session_rec }
29
+
30
+ session = @repository.find_by_session_id "unique-session-id"
31
+ session.should be_instance_of AuthenticationService::Session
32
+ session.ip_address.should eql "127.0.0.2"
33
+ end
34
+
35
+ it "should return nil for not existing session_id" do
36
+ @model_class.stub(:find_by_session_id) { nil }
37
+ @repository.find_by_session_id("unknown-session-id").should be_nil
38
+ end
39
+
40
+ it "should assign corresponding account" do
41
+ session = @repository.find_by_session_id "unique-session-id"
42
+ session.account.should_not be_nil
43
+ session.account.should be_instance_of AuthenticationService::Account
44
+ session.account.account_id.should eql @account_rec.id
45
+ end
46
+ end
47
+
48
+ describe "create" do
49
+ it "should create corresponding database record" do
50
+ session = AuthenticationService::Session.create_new to_account_model(@account_rec), "127.0.0.1"
51
+ @model_class.should_receive(:create!).with(:session_id => session.session_id,
52
+ :account_id => session.account.account_id,
53
+ :ip_address => session.ip_address,
54
+ :created_at => session.created_at,
55
+ :updated_at => session.last_time_used_at)
56
+
57
+ @repository.create session
58
+ end
59
+
60
+ it "should return created session instance" do
61
+ new_session = AuthenticationService::Session.create_new to_account_model(@account_rec), "127.0.0.1"
62
+ @model_class.stub(:create!)
63
+ session = @repository.create new_session
64
+ session.should be(new_session)
65
+ end
66
+ end
67
+
68
+ describe "touch" do
69
+ it "should set updated_at date to now" do
70
+ @model_class.should_receive(:find_by_session_id).with(@session_rec.session_id).and_return(@session_rec)
71
+ @session_rec.should_receive :touch
72
+ @repository.touch @session_rec.session_id
73
+ end
74
+ end
75
+
76
+ describe "destroy" do
77
+ before(:each) do
78
+ @session = to_session_model(@session_rec)
79
+ end
80
+
81
+ it "should destroy sessions record" do
82
+ @model_class.should_receive(:find_by_session_id).with(@session_rec.session_id).and_return(@session_rec)
83
+ @session_rec.should_receive(:destroy)
84
+ @repository.destroy(@session)
85
+ end
86
+
87
+ it "should return sessions model" do
88
+ @model_class.stub(:find_by_session_id).and_return(@session_rec)
89
+ @session_rec.stub(:destroy)
90
+ @repository.destroy(@session).should be @session
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe AuthenticationService::Persistance do
4
+ describe AuthenticationService::Persistance::ModelHelpers do
5
+ include AuthenticationService::Persistance::ModelHelpers
6
+
7
+ describe "to_account_model" do
8
+ it "should create new model instance" do
9
+ rec = mock :account, :id => 999, :email => "mail@domain.com", :password_hash => "password-hash"
10
+ account = to_account_model(rec)
11
+ account.account_id.should eql rec.id
12
+ account.email.should eql rec.email
13
+ account.password_hash.should eql rec.password_hash
14
+ end
15
+ end
16
+
17
+ describe "to_session_model" do
18
+ before(:each) do
19
+ @account = mock :account, :id => 999, :email => "mail@domain.com", :password_hash => "password-hash"
20
+ @session = mock :session, :id => 998, :session_id => "some-session-id",
21
+ :account => @account,
22
+ :ip_address => "127.0.0.3",
23
+ :created_at => DateTime.now - 3,
24
+ :updated_at => DateTime.now - 2
25
+ end
26
+
27
+ it "should return session model with corresponding properties assigned" do
28
+ model = to_session_model(@session)
29
+ model.should be_instance_of(AuthenticationService::Session)
30
+ model.session_id.should eql @session.session_id
31
+ model.ip_address.should eql @session.ip_address
32
+ model.created_at.should eql @session.created_at
33
+ model.last_time_used_at.should eql @session.updated_at
34
+ end
35
+
36
+ it "should also assign account" do
37
+ model = to_session_model(@session)
38
+ model.account.should be_instance_of AuthenticationService::Account
39
+ model.account.email.should eql "mail@domain.com"
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,169 @@
1
+ require 'spec_helper'
2
+
3
+ class RailsSessionsControllerActionsSpecController
4
+
5
+ class << self
6
+ attr_accessor :before_filter_args
7
+ def before_filter(sym, options)
8
+ @before_filter_args = {
9
+ :sym => sym,
10
+ :options => options
11
+ }
12
+ end
13
+ end
14
+
15
+ include AuthenticationService::Rails::SessionsControllerActions
16
+
17
+ attr_reader :session
18
+ attr_accessor :authentication_service, :current_session, :request, :params
19
+
20
+ def initialize
21
+ @session = {}
22
+ end
23
+ end
24
+
25
+ describe RailsSessionsControllerActionsSpecController do
26
+
27
+ before(:each) do
28
+ @session = mock(:session)
29
+ @authentication_service = mock(:authentication_service)
30
+ @controller = RailsSessionsControllerActionsSpecController.new
31
+ @controller.request = mock(:request)
32
+ @controller.authentication_service = @authentication_service
33
+
34
+ @controller.stub(:redirect_back)
35
+ @controller.request.stub("remote_ip") { "localhost" }
36
+ @session.stub(:session_id) { "some-session-id" }
37
+ end
38
+
39
+ describe "module included" do
40
+ it "should define filter :authenticate for destroy action" do
41
+ @controller.class.before_filter_args[:sym].should eql :authenticate
42
+ @controller.class.before_filter_args[:options][:only].should eql :destroy
43
+ end
44
+ end
45
+
46
+ describe "new" do
47
+ it "should do nothing" do
48
+ @controller.new
49
+ end
50
+ end
51
+
52
+ describe "create" do
53
+ describe "authentication successful" do
54
+ it "uses authentication_service to create new session" do
55
+ @authentication_service.should_receive(:authenticate_by_login).with("admin", "password", "localhost").and_return(@session)
56
+
57
+ @controller.params = {
58
+ :login => "admin",
59
+ :password => "password"
60
+ }
61
+ @controller.create
62
+ end
63
+
64
+ it "remembers new session_id in session" do
65
+ @authentication_service.stub(:authenticate_by_login).and_return(@session)
66
+
67
+ @controller.params = {
68
+ :login => "admin",
69
+ :password => "password"
70
+ }
71
+ @controller.create
72
+
73
+ @controller.session[:authenticated_session_id].should eql "some-session-id"
74
+ end
75
+
76
+ it "redirects redirect_back" do
77
+ @authentication_service.stub(:authenticate_by_login).and_return(@session)
78
+
79
+ @controller.should_receive :redirect_back
80
+
81
+ @controller.params = {
82
+ :login => "admin",
83
+ :password => "password"
84
+ }
85
+ @controller.create
86
+ end
87
+ end
88
+
89
+ describe "authentication failed" do
90
+ before(:each) do
91
+ @authentication_service.stub(:authenticate_by_login).and_return(nil)
92
+ @format = mock(:format)
93
+ @format.stub(:json)
94
+ @format.stub(:html)
95
+ @controller.should_receive(:respond_to) do |&block| block.call(@format) end
96
+ end
97
+
98
+ describe "for html format" do
99
+ it "should render :new and return unauthorized status" do
100
+ @format.should_receive(:html) do |&block| block.call end
101
+
102
+ @controller.should_receive(:render).with(:action => :new, :status => :unauthorized)
103
+
104
+ @controller.params = {
105
+ :login => "admin",
106
+ :password => "password"
107
+ }
108
+ @controller.create
109
+ end
110
+ end
111
+
112
+ describe "for json format" do
113
+ it "should render :nothing and return unauthorized status" do
114
+ @format.should_receive(:json) do |&block| block.call end
115
+
116
+ @controller.should_receive(:render).with(:nothing => true, :status => :unauthorized)
117
+
118
+ @controller.params = {
119
+ :login => "admin",
120
+ :password => "password"
121
+ }
122
+ @controller.create
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ describe "POST 'destroy'" do
129
+ before(:each) do
130
+ @format = mock(:format)
131
+ @format.stub(:json)
132
+ @format.stub(:html)
133
+ @controller.should_receive(:respond_to) do |&block| block.call(@format) end
134
+
135
+ @controller.current_session = @session
136
+ @authentication_service.stub(:sign_out)
137
+
138
+ @controller.stub(:sign_in_url) { '/sign-in' }
139
+ @controller.stub(:reset_session)
140
+ end
141
+
142
+ it "should use authentication_service to sign-off" do
143
+ @authentication_service.should_receive(:sign_out).with(@session)
144
+ @controller.destroy
145
+ end
146
+
147
+ it "should reset session" do
148
+ @controller.should_receive(:reset_session)
149
+
150
+ @controller.destroy
151
+ end
152
+
153
+ describe "for html" do
154
+ it "should redirect to sign_in_url" do
155
+ @format.should_receive(:html) do |&block| block.call end
156
+ @controller.should_receive(:redirect_to).with("/sign-in")
157
+ @controller.destroy
158
+ end
159
+ end
160
+
161
+ describe "for json" do
162
+ it "should render json with sign_in_url" do
163
+ @format.should_receive(:json) do |&block| block.call end
164
+ @controller.should_receive(:render).with(:json => {'navigate-to' => '/sign-in'})
165
+ @controller.destroy
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,207 @@
1
+ require 'spec_helper'
2
+
3
+ class AuthenticationSpecController
4
+ include AuthenticationService::Rails
5
+
6
+ # authentication_service { :sign_in_action => '/sign-in',
7
+ # :sign_out_action => '/sign-out'}
8
+
9
+ attr_reader :session
10
+ attr_accessor :request
11
+
12
+ def initialize
13
+ @session = {}
14
+ end
15
+
16
+ attr_accessor :respond_to_format
17
+ def respond_to(&block)
18
+ yield(@respond_to_format)
19
+ end
20
+ end
21
+
22
+ describe AuthenticationService::Rails do
23
+ before(:each) do
24
+ @controller = AuthenticationSpecController.new
25
+ @controller.request = mock(:request)
26
+ end
27
+
28
+ before(:each) do
29
+ @authentication_service = mock(:authentication_service)
30
+ @controller.authentication_service = @authentication_service
31
+ end
32
+
33
+ describe "self.class.authentication_service" do
34
+ it "should fail if options does not have account" do
35
+ session_class = mock(:session_class)
36
+ lambda {
37
+ AuthenticationSpecController.authentication_service :account => nil, :session => session_class
38
+ }.should raise_error(RuntimeError)
39
+ end
40
+
41
+ it "should fail if options does not have session" do
42
+ account_class = mock(:account_class)
43
+ lambda {
44
+ AuthenticationSpecController.authentication_service :account => nil, :account => account_class
45
+ }.should raise_error(RuntimeError)
46
+ end
47
+
48
+ it "should assign account and session classes" do
49
+ account_class = mock(:account_class)
50
+ session_class = mock(:session_class)
51
+ AuthenticationSpecController.authentication_service :account => account_class, :session => session_class
52
+ AuthenticationSpecController.account_class.should be account_class
53
+ AuthenticationSpecController.session_class.should be session_class
54
+ end
55
+ end
56
+
57
+ describe "authentication_service=" do
58
+ it "should return instance with configured account and session classes" do
59
+ account_class = mock(:account_class)
60
+ session_class = mock(:session_class)
61
+ AuthenticationSpecController.authentication_service :account => account_class, :session => session_class
62
+ @controller.authentication_service = nil
63
+ @controller.authentication_service.accounts_repository.model_class.should be account_class
64
+ @controller.authentication_service.sessions_repository.model_class.should be session_class
65
+ end
66
+ end
67
+
68
+ describe "authenticated?" do
69
+ it "should return true if current sesion is not nil" do
70
+ @controller.current_session = mock(:session, :blank? => false)
71
+ @controller.authenticated?.should be_true
72
+ end
73
+
74
+ it "should return false if current session is nil" do
75
+ @controller.current_session = mock(:session, :blank? => true)
76
+ @controller.authenticated?.should be_false
77
+ end
78
+ end
79
+
80
+ describe "authenticate" do
81
+ it "should authenticate_from_session if there is authenticated session id" do
82
+ @controller.session[:authenticated_session_id] = "some-session-id"
83
+ @controller.should_receive(:authenticate_from_session) {
84
+ @controller.current_session = mock(:session, :blank? => false)
85
+ }
86
+ @controller.should_not_receive(:redirect_not_authenticated)
87
+ @controller.authenticate
88
+ end
89
+
90
+ it "should redirect_not_authenticated if not authenticated" do
91
+ @controller.current_session = mock(:session, :blank? => true)
92
+ @controller.should_receive(:redirect_not_authenticated)
93
+ @controller.authenticate
94
+ end
95
+
96
+ it "should not redirect if authenticated" do
97
+ @controller.current_session = mock(:session, :blank? => false)
98
+ @controller.should_not_receive(:redirect_not_authenticated)
99
+ @controller.authenticate
100
+ end
101
+ end
102
+
103
+ describe "authenticate_from_session" do
104
+ before(:each) do
105
+ @controller.authentication_service = mock(:authentication_service)
106
+ @controller.session[:authenticated_session_id] = "authenticated-session-id"
107
+ @controller.request.stub(:remote_ip).and_return("192.168.48.1")
108
+ end
109
+
110
+ it "uses authentication-service to authenticate by session-id" do
111
+ @controller.request.should_receive(:remote_ip).and_return("192.168.48.1")
112
+ @controller.authentication_service.should_receive(:authenticate_by_session_id).with("authenticated-session-id", "192.168.48.1")
113
+ @controller.send(:authenticate_from_session)
114
+ end
115
+
116
+ it "assigns current_session" do
117
+ authenticated_session = mock(:authenticated_session)
118
+ @controller.authentication_service.
119
+ should_receive(:authenticate_by_session_id).
120
+ with("authenticated-session-id", "192.168.48.1").
121
+ and_return(authenticated_session)
122
+ @controller.send(:authenticate_from_session)
123
+ @controller.current_session.should be authenticated_session
124
+ end
125
+ end
126
+
127
+ describe "redirect_not_authenticated" do
128
+ before(:each) do
129
+ @controller.request.should_receive(:fullpath).and_return('/current-path')
130
+ @controller.should_receive(:sign_in_url).and_return("/sign-in")
131
+ @controller.stub(:redirect_to)
132
+ end
133
+
134
+ it "should remember current path in session" do
135
+ @controller.send(:redirect_not_authenticated)
136
+ @controller.session[:return_to].should eql '/current-path'
137
+ end
138
+
139
+ it "should redirect to sign_in_url" do
140
+ @controller.should_receive(:redirect_to).with("/sign-in")
141
+ @controller.send(:redirect_not_authenticated)
142
+ end
143
+ end
144
+
145
+ describe "redirect_back" do
146
+ before(:each) do
147
+ @format = mock(:format)
148
+ @format.stub(:html)
149
+ @format.stub(:json)
150
+ @controller.respond_to_format = @format
151
+ end
152
+
153
+ describe "for html format" do
154
+ before(:each) do
155
+ @format.should_receive(:html) do |&block| block.call end
156
+ end
157
+
158
+ it "should redirect to previously saved return path" do
159
+ @controller.session[:return_to] = "/return-path"
160
+ @controller.should_receive(:redirect_to).with("/return-path")
161
+ @controller.send(:redirect_back)
162
+ end
163
+
164
+ it "should redirect to root_url if no saved return path" do
165
+ @controller.should_receive(:root_url).and_return("/root-url")
166
+ @controller.should_receive(:redirect_to).with("/root-url")
167
+ @controller.send(:redirect_back)
168
+ end
169
+
170
+ it "should clear saved return path" do
171
+ @controller.session[:return_to] = "/return-path"
172
+ @controller.stub(:redirect_to)
173
+ @controller.send(:redirect_back)
174
+ @controller.session[:return_to].should be_nil
175
+ end
176
+ end
177
+
178
+ describe "for json format" do
179
+ before(:each) do
180
+ @format.should_receive(:json) do |&block| block.call end
181
+ end
182
+
183
+ it "should render json with previously saved return path" do
184
+ @controller.session[:return_to] = "/return-path"
185
+ @controller.should_receive(:render) do |rendered_with|
186
+ rendered_with[:json]['navigate-to'].should eql '/return-path'
187
+ end
188
+ @controller.send(:redirect_back)
189
+ end
190
+
191
+ it "should render json with root_url if no saved return path" do
192
+ @controller.should_receive(:root_url).and_return("/root-url")
193
+ @controller.should_receive(:render) do |rendered_with|
194
+ rendered_with[:json]['navigate-to'].should eql '/root-url'
195
+ end
196
+ @controller.send(:redirect_back)
197
+ end
198
+
199
+ it "should clear saved return path" do
200
+ @controller.session[:return_to] = "/return-path"
201
+ @controller.stub(:render)
202
+ @controller.send(:redirect_back)
203
+ @controller.session[:return_to].should be_nil
204
+ end
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Session do
4
+ describe "create_new" do
5
+ it "should assign account and ip_address" do
6
+ account = mock(:account)
7
+ session = Session.create_new(account, "localhost")
8
+ session.account.should be account
9
+ session.ip_address.should eql "localhost"
10
+ end
11
+
12
+ it "should generate new random session_id" do
13
+ session1 = Session.create_new(mock(:account), "localhost")
14
+ session2 = Session.create_new(mock(:account), "localhost")
15
+
16
+ session1.session_id.should_not be_nil
17
+ session2.session_id.should_not be_nil
18
+
19
+ session1.session_id.should_not eql session2.session_id
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+
3
+ # Set up gems listed in the Gemfile.
4
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
5
+
6
+ require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
7
+
8
+ require 'rspec'
9
+ require 'authentication-service'
10
+
11
+ # This file was generated by the `rspec --init` command. Conventionally, all
12
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
13
+ # Require this file using `require "spec_helper.rb"` to ensure that it is only
14
+ # loaded once.
15
+ #
16
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
17
+ RSpec.configure do |config|
18
+ include AuthenticationService
19
+
20
+ config.treat_symbols_as_metadata_keys_with_true_values = true
21
+ config.run_all_when_everything_filtered = true
22
+ config.filter_run :focus
23
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: authentication-service
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.a1
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Evgeny Myasishchev
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70191597925800 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70191597925800
25
+ description: Provides authentication related stuff.
26
+ email:
27
+ - evgeny.myasishchev@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - .gitignore
33
+ - .rspec
34
+ - .rvmrc
35
+ - Gemfile
36
+ - README
37
+ - Rakefile
38
+ - authentication-service.gemspec
39
+ - lib/authentication-service.rb
40
+ - lib/authentication-service/account.rb
41
+ - lib/authentication-service/base.rb
42
+ - lib/authentication-service/persistance.rb
43
+ - lib/authentication-service/persistance/accounts-repository.rb
44
+ - lib/authentication-service/persistance/sessions-repository.rb
45
+ - lib/authentication-service/rails.rb
46
+ - lib/authentication-service/rails/sessions-controller-actions.rb
47
+ - lib/authentication-service/session.rb
48
+ - lib/authentication-service/version.rb
49
+ - spec/lib/account_spec.rb
50
+ - spec/lib/base_spec.rb
51
+ - spec/lib/persistance/accounts_repository_spec.rb
52
+ - spec/lib/persistance/sessions_repository_spec.rb
53
+ - spec/lib/persistance_spec.rb
54
+ - spec/lib/rails_sessions_controller_actions_spec.rb
55
+ - spec/lib/rails_spec.rb
56
+ - spec/lib/session_spec.rb
57
+ - spec/spec_helper.rb
58
+ homepage: ''
59
+ licenses: []
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>'
74
+ - !ruby/object:Gem::Version
75
+ version: 1.3.1
76
+ requirements: []
77
+ rubyforge_project: authentication-service
78
+ rubygems_version: 1.8.15
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: Authentication service for Rails (and not only)
82
+ test_files:
83
+ - spec/lib/account_spec.rb
84
+ - spec/lib/base_spec.rb
85
+ - spec/lib/persistance/accounts_repository_spec.rb
86
+ - spec/lib/persistance/sessions_repository_spec.rb
87
+ - spec/lib/persistance_spec.rb
88
+ - spec/lib/rails_sessions_controller_actions_spec.rb
89
+ - spec/lib/rails_spec.rb
90
+ - spec/lib/session_spec.rb
91
+ - spec/spec_helper.rb