authentication-service 0.0.1.a1

Sign up to get free protection for your applications and to get access to all the features.
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