Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added skeleton for oa-identity as well as the basics for the model mi…

…xin.
  • Loading branch information...
commit 5225b20f3f8226c53d4c5170fa3339b804f76b97 1 parent b2bcc84
@mbleigh mbleigh authored
View
2  .rspec
@@ -1,2 +1,2 @@
+--format=nested
--colour
---format=nested
View
8 Gemfile
@@ -1,9 +1,3 @@
source "http://rubygems.org"
-gem 'oa-core', :path => File.expand_path('../../oa-core/', __FILE__)
-
-# Will automatically pull in this gem and all its
-# dependencies specified in the gemspec
-gem 'oa-identity', :path => File.expand_path("..", __FILE__)
-
-eval File.read(File.join(File.dirname(__FILE__), '../development_dependencies.rb'))
+gemspec
View
19 LICENSE
@@ -1,19 +0,0 @@
-Copyright (c) 2010-2011 Michael Bleigh and Intridea, Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
View
66 README.markdown
@@ -1,51 +1,41 @@
-# OmniAuth::Identity
+# OmniAuth Identity
-`OmniAuth::Identity` brings a traditional e-mail/login based sign in
-flow to OmniAuth without sacrificing the simplicity and pattern
-employed by OmniAuth's external authenticating brethren. It allows
-users to create "identities" that consist of a user's basic info,
-a login key, and a password.
-
-## Installation
-
-To install omniauth as a suite of gems:
-
- gem install omniauth
-
-To install just the login/password flow in the "identity" gem:
-
- gem install oa-identity
+The OmniAuth Identity gem provides a way for applications to utilize a
+traditional login/password based authentication system without the need
+to give up the simple authentication flow provided by OmniAuth. Identity
+is designed on purpose to be as featureless as possible: it provides the
+basic construct for user management and then gets out of the way.
## Usage
-You can use `OmniAuth::Identity` just like any other provider: that's
-the whole point!
+You use `oa-identity` just like you would any other OmniAuth provider:
+as a Rack middleware. The basic setup would look something like this:
use OmniAuth::Builder do
- provider :identity, :key => :email, :attributes => [:name, :email, :location]
+ provider :identity
end
-Now your users will be able to create or sign into an identity by
-visiting `/auth/identity`. Once they have done so, your application
-will receive a callback at `/auth/identity/callback` just like it
-would with any other OmniAuth strategy.
-
-## ORMs
+Next, you need to create a model that will be able to persist the
+information provided by the user. By default, this model should be a
+class called `Identity` and should respond to the following API:
-`OmniAuth::Identity` supports multiple ORMs:
+ Identity.create(
+ :name => 'x',
+ :password => 'y',
+ :confirm_password => 'y'
+ )
-* ActiveRecord
-* MongoMapper
+ identity = Identity.authenticate('key', 'password')
+ # => Identity instance if correct
+ # => false if incorrect
-By default, it will try to detect which if any of the ORMs your app
-is using in the order specified above. You can also explicitly set
-the ORM by making this call before you instantiate the middleware:
+ identity.user_info # => {'name' => '...', 'nickname' => '...'}
+ identity.uid # => must be unique to the application
- require 'omniauth/identity/orm/ormname'
+To make things easier, you can inherit your model from the ones provided
+for popular ORMs which will automatically provide the default setup
+necessary. For example:
-Where `ormname` is the same as the **gem name** of the ORM you would
-like to use.
-
-To implement a custom ORM, it's quite simple. You simply need to define
-the `OmniAuth::Identity` class such that it adheres to the interface set
-forth in `OmniAuth::Identity::Interface`.
+ class Identity < OmniAuth::Identity::Model::ActiveRecord
+ login_key :nickname
+ end
View
12 Rakefile
@@ -1,14 +1,8 @@
-require 'rubygems'
require 'bundler'
-Bundler.setup
-require 'rake'
-
-require 'mg'
-MG.new('oa-identity.gemspec')
+Bundler::GemHelper.install_tasks
require 'rspec/core/rake_task'
-RSpec::Core::RakeTask.new(:spec) do |s|
- s.rspec_opts = "--format=#{ENV['RSPEC_FORMAT'] || 'nested'} --colour"
-end
+RSpec::Core::RakeTask.new(:spec)
task :default => :spec
+task :test => :spec
View
1  autotest/discover.rb
@@ -1 +0,0 @@
-Autotest.add_discovery { "rspec2" }
View
1  lib/oa-identity.rb
@@ -0,0 +1 @@
+require 'omniauth/identity'
View
7 lib/omniauth/identity.rb
@@ -6,7 +6,12 @@ module Strategies
end
module Identity
+ autoload :Model, 'omniauth/identity/model'
+ module Models
+ autoload :ActiveRecord, 'omniauth/identity/models/active_record'
+ # autoload :MongoMapper, 'omniauth/identity/models/mongo_mapper'
+ # autoload :Mongoid, 'omniauth/identity/models/mongoid'
+ end
end
end
-
View
23 lib/omniauth/identity/interface.rb
@@ -1,23 +0,0 @@
-module OmniAuth
- module Identity
- module Interface
- extend ActiveSupport::Concern
-
- module ClassMethods
- # Taking a hash of identifying attribute data, this should
- # return an OmniAuth-compatible hash with the `uid` and
- # appropriate `user_info` set.
- def create(hash = {})
- raise NotImplementedError, "Your identity provider must implement a .create method."
- end
-
- # Taking an attribute name (usually "email" or "screen_name"),
- # this method should return an OmniAuth-compatible authentication
- # hash.
- def identify(key, value, password)
- raise NotImplementedError, "Your identity provider must implement a .identify method."
- end
- end
- end
- end
-end
View
66 lib/omniauth/identity/model.rb
@@ -0,0 +1,66 @@
+module OmniAuth
+ module Identity
+ # This module provides an includable interface for implementing the
+ # necessary API for OmniAuth Identity to properly locate identities
+ # and provide all necessary information. All methods marked as
+ # abstract must be implemented in the including class for things to
+ # work properly.
+ module Model
+ def self.included(base)
+ base.extend ClassMethods
+ end
+
+ module ClassMethods
+ # Locate an identity given its unique login key.
+ #
+ # @abstract
+ # @param [String] key The unique login key.
+ # @return [Model] An instance of the identity model class.
+ def locate(key)
+ raise NotImplementedError
+ end
+
+ # Authenticate a user with the given key and password.
+ #
+ # @param [String] key The unique login key provided for a given identity.
+ # @param [String] password The presumed password for the identity.
+ # @return [Model] An instance of the identity model class.
+ def authenticate(key, password)
+ locate(key).authenticate(password)
+ end
+ end
+
+ # Returns self if the provided password is correct, false
+ # otherwise.
+ #
+ # @abstract
+ # @param [String] password The password to check.
+ # @return [self or false] Self if authenticated, false if not.
+ def authenticate(password)
+ raise NotImplementedError
+ end
+
+ SCHEMA_ATTRIBUTES = %w(name email nickname first_name last_name location description image phone)
+ # A hash of as much of the standard OmniAuth schema as is stored
+ # in this particular model. By default, this will call instance
+ # methods for each of the attributes it needs in turn, ignoring
+ # any for which `#respond_to?` is `false`.
+ #
+ # If `first_name`, `nickname`, and/or `last_name` is provided but
+ # `name` is not, it will be automatically calculated.
+ #
+ # @return [Hash] A string-keyed hash of user information.
+ def user_info
+ info = SCHEMA_ATTRIBUTES.inject({}) do |hash,attribute|
+ hash[attribute] = send(attribute) if respond_to?(attribute)
+ hash
+ end
+
+ info['name'] ||= [info['first_name'], info['last_name']].join(' ').strip if info['first_name'] || info['last_name']
+ info['name'] ||= info['nickname']
+
+ info
+ end
+ end
+ end
+end
View
11 lib/omniauth/identity/models/active_record.rb
@@ -0,0 +1,11 @@
+require 'active_record'
+
+module OmniAuth
+ module Identity
+ module Models
+ class ActiveRecord << ::ActiveRecord::Base
+ include OmniAuth::Identity::Model
+ end
+ end
+ end
+end
View
27 lib/omniauth/identity/orm/mongo_mapper.rb
@@ -1,27 +0,0 @@
-require 'omniauth/identity'
-
-module OmniAuth
- module Identity
- class Model
- include OmniAuth::Identity::Interface
- include MongoMapper::Document
-
- key :uid, String
- key :crypted_password, String
- key :crypted_password_salt,
-
- before_validation(:on => :create) do
- self.uid ||= UUID.generate(:compact)
- end
-
- def auth_hash
- {
- 'uid' => self.uid,
- 'user_info' => {
-
- }
- }
- end
- end
- end
-end
View
36 lib/omniauth/strategies/identity.rb
@@ -1,43 +1,7 @@
-require 'omniauth/identity'
-
module OmniAuth
module Strategies
class Identity
include OmniAuth::Strategy
-
- def initialize(app, options = {}, &block)
- options[:key] ||= 'nickname'
- super(app, :identity, options, &block)
- end
-
- def request_phase
- OmniAuth::Form.build(:title => (self.options[:title] || 'Identify Yourself'), :url => callback_path) do
- text_field 'Login', 'key'
- password_field 'Password', 'password'
-
- html <<-HTML
- <a id='toggle_register' href='javascript:document.getElementById("toggle_register").style.display="none";document.getElementById("register").style.display = "block"'>Create a New Identity</a>
- HTML
-
- fieldset("Create Identity", :id => 'register', :style => 'display:none;') do
- password_field 'Repeat Password', 'register[password_confirmation]'
- text_field 'E-Mail', 'register[email]'
- text_field 'Name', 'register[name]'
- end
- end.to_response
- end
-
- def callback_phase
- if auth_hash
- super
- else
- fail!(:invalid_credentials)
- end
- end
-
- def auth_hash
- @hash ||= OmniAuth::Identity::Model.identify(options[:key],request.params['key'],request.params['password'])
- end
end
end
end
View
41 oa-identity.gemspec
@@ -1,21 +1,26 @@
-require 'rubygems'
-
-version = File.open(File.dirname(__FILE__) + '/../VERSION', 'r').read.strip
+# -*- encoding: utf-8 -*-
+require File.expand_path('../../lib/omniauth/version', __FILE__)
Gem::Specification.new do |gem|
- gem.name = "oa-identity"
- gem.version = version
- gem.summary = %Q{Login and password authentication for OmniAuth.}
- gem.description = %Q{Login and password authentication for OmniAuth.}
- gem.email = "michael@intridea.com"
- gem.homepage = "http://github.com/intridea/omniauth"
- gem.authors = ["Michael Bleigh"]
-
- gem.files = Dir.glob("{lib}/**/*") + %w(README.rdoc LICENSE)
-
- gem.add_dependency 'oa-core', version
- gem.add_dependency 'activesupport', '~> 3.0.0'
- gem.add_dependency 'uuid'
-
- eval File.read(File.join(File.dirname(__FILE__), '../development_dependencies.rb'))
+ gem.add_runtime_dependency 'oa-core', OmniAuth::Version::STRING
+ gem.add_development_dependency 'maruku', '~> 0.6'
+ gem.add_development_dependency 'simplecov', '~> 0.4'
+ gem.add_development_dependency 'rack-test', '~> 0.5'
+ gem.add_development_dependency 'rake', '~> 0.8'
+ gem.add_development_dependency 'rspec', '~> 2.5'
+ gem.add_development_dependency 'yard', '~> 0.6'
+ gem.add_development_dependency 'ZenTest', '~> 4.5'
+ gem.name = 'oa-identity'
+ gem.version = OmniAuth::Version::STRING
+ gem.description = %q{Internal authentication handlers for OmniAuth.}
+ gem.summary = gem.description
+ gem.email = ['michael@intridea.com', 'sferik@gmail.com']
+ gem.homepage = 'http://github.com/intridea/omniauth'
+ gem.authors = ['Michael Bleigh', 'Erik Michaels-Ober']
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
+ gem.files = `git ls-files`.split("\n")
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ gem.require_paths = ['lib']
+ gem.required_rubygems_version = Gem::Requirement.new('>= 1.3.6') if gem.respond_to? :required_rubygems_version=
end
+
View
60 spec/omniauth/identity/model_spec.rb
@@ -0,0 +1,60 @@
+require 'spec_helper'
+
+class ExampleModel
+ include OmniAuth::Identity::Model
+end
+
+describe OmniAuth::Identity::Model do
+ context 'Class Methods' do
+ subject{ ExampleModel }
+
+ describe '.locate' do
+ it('should be abstract'){ lambda{ subject.locate('abc') }.should raise_error(NotImplementedError) }
+ end
+
+ describe '.authenticate' do
+ it 'should call locate and then authenticate' do
+ mocked_instance = mock('ExampleModel', :authenticate => 'abbadoo')
+ subject.should_receive(:locate).with('example').and_return(mocked_instance)
+ subject.authenticate('example','pass').should == 'abbadoo'
+ end
+ end
+ end
+
+ context 'Instance Methods' do
+ subject{ ExampleModel.new }
+
+ describe '#authenticate' do
+ it('should be abstract'){ lambda{ subject.authenticate('abc') }.should raise_error(NotImplementedError) }
+ end
+
+ describe '#user_info' do
+ it 'should include attributes that are set' do
+ subject.stub!(:name).and_return('Bob Bobson')
+ subject.stub!(:nickname).and_return('bob')
+
+ subject.user_info.should == {
+ 'name' => 'Bob Bobson',
+ 'nickname' => 'bob'
+ }
+ end
+
+ it 'should automatically set name off of first and last name' do
+ subject.stub!(:first_name).and_return('Bob')
+ subject.stub!(:last_name).and_return('Bobson')
+ subject.user_info['name'].should == 'Bob Bobson'
+ end
+
+ it 'should automatically set name off of nickname' do
+ subject.stub!(:nickname).and_return('bob')
+ subject.user_info['name'] == 'bob'
+ end
+
+ it 'should not overwrite a provided name' do
+ subject.stub!(:name).and_return('Awesome Dude')
+ subject.stub!(:first_name).and_return('Frank')
+ subject.user_info['name'].should == 'Awesome Dude'
+ end
+ end
+ end
+end
View
59 spec/omniauth/strategies/identity_spec.rb
@@ -1,59 +0,0 @@
-require 'spec_helper'
-
-module OmniAuth
- module Identity
- class Model
- end
- end
-end
-
-describe OmniAuth::Strategies::Identity do
- def app
- b = Rack::Builder.new
- b.use Rack::Session::Cookie
- b.use OmniAuth::Strategies::Identity, @options
- b.run @rack_app || lambda{|env| [@status || 404, {'env' => env}, ["Boing"]]}
- b.to_app
- end
-
- before(:each) do
- OmniAuth.config.on_failure = lambda{|env| [500, {}, [env['omniauth.error.type'].to_s]]}
- @options = {:key => 'nickname'}
- end
-
- context 'request phase' do
- it 'should display a form' do
- get '/auth/identity'
- last_response.body.should be_include("<form")
- end
-
- it 'should request the specified key' do
- pending "Let's get the basics working first."
- @options = {:key => 'email'}
- get '/auth/identity'
- last_response.body.should be_include("Email")
- end
- end
-
- context 'callback phase' do
- context 'with an existing user' do
- it 'should return the user info if a correct password is specified' do
- OmniAuth::Identity::Model.should_receive(:identify).
- with('nickname','existing','correct').
- and_return({'uid' => '123abc'})
- post '/auth/identity/callback', :key => 'existing', :password => 'correct'
- last_response.headers['env']['omniauth.auth']['uid'].should == '123abc'
- end
-
- it 'should fail with :invalid_credentials if no user exists' do
- OmniAuth::Identity::Model.should_receive(:identify).
- with('nickname','existing','wrong').
- and_return(nil)
- post '/auth/identity/callback', :key => 'existing', :password => 'wrong'
-
- last_response.status.should == 500
- last_response.body.should == 'invalid_credentials'
- end
- end
- end
-end
View
19 spec/spec_helper.rb
@@ -1,15 +1,12 @@
-require 'rubygems'
-require 'bundler'
-Bundler.setup
-
+require 'simplecov'
+SimpleCov.start
require 'rspec'
-require 'rspec/autorun'
require 'rack/test'
-require 'webmock/rspec'
-
-include Rack::Test::Methods
-include WebMock
-
require 'omniauth/identity'
+require 'omniauth/test'
+
+RSpec.configure do |config|
+ config.include Rack::Test::Methods
+ config.extend OmniAuth::Test::StrategyMacros, :type => :strategy
+end
-WebMock.disable_net_connect!
Please sign in to comment.
Something went wrong with that request. Please try again.