Skip to content

Commit

Permalink
Merge pull request #1 from barttenbrinke/nested_attributes
Browse files Browse the repository at this point in the history
Added support for Nested attributes in MongoBD
  • Loading branch information
trekdemo committed Apr 15, 2015
2 parents ffe9661 + a4314bd commit 4b34532
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 8 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -17,7 +17,7 @@ To use the Moped authenticator, configure it in your cas.yml:
pepper: "suffix of the password" # optional
extra_attributes:
email: "email_database_column"
fullname: "displayname_database_column"
fullname: "user_info.full_name"

## Contributing to casino-moped_authenticator

Expand Down
2 changes: 1 addition & 1 deletion casino-moped_authenticator.gemspec
Expand Up @@ -25,6 +25,6 @@ Gem::Specification.new do |s|
s.add_runtime_dependency 'moped', '~> 1.5'
s.add_runtime_dependency 'unix-crypt', '~> 1.1'
s.add_runtime_dependency 'bcrypt', '~> 3.0'
s.add_runtime_dependency 'casino', '~> 2.0'
s.add_runtime_dependency 'casino', '~> 3.0'
s.add_runtime_dependency 'phpass-ruby', '~> 0.1'
end
32 changes: 26 additions & 6 deletions lib/casino/moped_authenticator.rb
Expand Up @@ -4,28 +4,48 @@
require 'phpass'

class CASino::MopedAuthenticator

# @param [Hash] options
def initialize(options)
@options = options
@connection = Moped::Session.connect(options[:database_url])
end

def validate(username, password)
return false unless user = collection.find(@options[:username_column] => username).first
password_from_database = user[@options[:password_column]]
user = collection.find(@options[:username_column] => username).first
return false unless user

username_from_database = get_nested(user, @options[:username_column], true)
password_from_database = get_nested(user, @options[:password_column], true)

if valid_password?(password, password_from_database)
{ username: user[@options[:username_column]], extra_attributes: extra_attributes(user) }
{ username: username_from_database, extra_attributes: extra_attributes(user) }
else
false
end
end

private

def get_nested(item, key_string, first = false)
return_item = item.dup

return return_item unless item.is_a?(Hash)

key_string.split('.').each do |key_part|
result = return_item[key_part]
result = result[0] if result.is_a?(Array) && first
return_item = result
end

return_item
end

def valid_password?(password, password_from_database)
return false if password_from_database.to_s.strip == ''

# SHA256 password if enabled
password = Digest::SHA256.hexdigest(password) if @options[:additional_digest] && @options[:additional_digest] == 'sha256'

magic = password_from_database.split('$')[1]
case magic
when /\A2a?\z/
Expand All @@ -47,12 +67,12 @@ def valid_password_with_unix_crypt?(password, password_from_database)
end

def valid_password_with_phpass?(password, password_from_database)
Phpass.new().check(password, password_from_database)
Phpass.new.check(password, password_from_database)
end

def extra_attributes(user)
extra_attributes_option.each_with_object({}) do |(attribute_name, database_column), attributes|
value = user[database_column]
value = get_nested(user, database_column)
value = value.to_s if value.is_a?(Moped::BSON::ObjectId)
attributes[attribute_name] = value
end
Expand Down
92 changes: 92 additions & 0 deletions spec/casino_core/nested_attributes_spec.rb
@@ -0,0 +1,92 @@
require 'spec_helper'
require 'casino/moped_authenticator'
require 'digest'
require 'bcrypt'

describe 'Nested Attributes' do

let(:options) do
{
database_url: 'mongodb://localhost:27017/my_nested_db?safe=true',
collection: 'users',
username_column: 'emails.address',
password_column: 'services.password.bcrypt',
additional_digest: 'sha256',
extra_attributes: { roles: 'roles' }
}
end

let(:password) { 'testpassword' }
let(:bcrypted_password) { BCrypt::Password.create(Digest::SHA256.hexdigest(password)) } # This is standard in Node Bcrypt
let(:email_address) { 'test@example.com' }
let(:roles) { %w(admin user) }

subject { CASino::MopedAuthenticator.new(options) }

before do
create_user(email_address, bcrypted_password, roles: roles)
end
after { @session.drop }

describe '#validate' do

context 'valid username' do
context 'valid password' do
it 'returns the username' do
subject.validate(email_address, password)[:username].should eq(email_address)
end

it 'returns the extra attributes' do
subject.validate(email_address, password)[:extra_attributes][:roles].should eq(roles)
end
end

context 'invalid password' do
it 'returns false' do
subject.validate(email_address, 'wrong').should eq(false)
end
end

context 'NULL password field' do
it 'returns false' do
update_user_pw 'test', nil

subject.validate(email_address, 'wrong').should eq(false)
end
end

context 'empty password field' do
it 'returns false' do
update_user_pw 'test', ''

subject.validate(email_address, 'wrong').should eq(false)
end
end
end

context 'invalid username' do
it 'returns false' do
subject.validate('wrong@example.com', password).should eq(false)
end
end
end

def create_user(email_address, bcrypted_password, extra = {})
session[options[:collection]].insert({
emails: [{ address: email_address }],
services: { password: { bcrypt: bcrypted_password } }
}.merge(extra))
end

def update_user_pw(username, new_password)
session[options[:collection]].find(username: username).update(password: new_password)
end

def user_with_name(username)
session[options[:collection]].find(username: username).first
end

def session
@session ||= ::Moped::Session.connect(options[:database_url])
end
end

0 comments on commit 4b34532

Please sign in to comment.