Skip to content

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
  • 4 commits
  • 55 files changed
  • 0 commit comments
  • 2 contributors
Commits on Nov 22, 2011
@thecatwasnot thecatwasnot Tweak and setup, org Gemfile a little cd1919c
@thecatwasnot thecatwasnot Initialize gard with a basic Guardfile 59a384e
@thecatwasnot thecatwasnot User authentication from web
Devise provides authentication
Users may authenticate using username or email
System requires unique username and email
2a7df1f
Commits on Nov 23, 2011
@thecatwasnot thecatwasnot Oauth authentication fixes #1
Create client applications
Oauth web flow authorization
Rework oauth steps to use Rack::Test instead of hack Capybara
Pull oauth authentication steps out into helpers
Make user findable by access_token param
Add user to access grant by default
Re-hack capybara some to keep logged in session
Cries for re-work, but this whole area needs some rework
it'll be left for now.  Ain't pretty, THANKS CAPYBARA >:(
Oauth work based on:
http://www.railsatwork.com/2010/10/implementing-oauth-provider-part-1.html
Oauth v2.0 draft:
http://tools.ietf.org/html/draft-ietf-oauth-v2-22
7065946
Showing with 1,095 additions and 300 deletions.
  1. +14 −23 Gemfile
  2. +25 −16 Gemfile.lock
  3. +28 −0 Guardfile
  4. +3 −0 app/assets/javascripts/public.js.coffee
  5. +3 −0 app/assets/stylesheets/public.css.scss
  6. +12 −0 app/controllers/client_applications_controller.rb
  7. +25 −0 app/controllers/oauth_controller.rb
  8. +5 −0 app/controllers/public_controller.rb
  9. +2 −0 app/helpers/public_helper.rb
  10. +18 −0 app/models/access_grant.rb
  11. +31 −0 app/models/client_application.rb
  12. +29 −0 app/models/user.rb
  13. +12 −0 app/views/client_applications/new.html.haml
  14. +5 −0 app/views/client_applications/show.html.haml
  15. +9 −0 app/views/devise/confirmations/new.html.haml
  16. +4 −0 app/views/devise/mailer/confirmation_instructions.html.haml
  17. +6 −0 app/views/devise/mailer/reset_password_instructions.html.haml
  18. +5 −0 app/views/devise/mailer/unlock_instructions.html.haml
  19. +14 −0 app/views/devise/passwords/edit.html.haml
  20. +9 −0 app/views/devise/passwords/new.html.haml
  21. +31 −0 app/views/devise/registrations/edit.html.haml
  22. +21 −0 app/views/devise/registrations/new.html.haml
  23. +16 −0 app/views/devise/sessions/new.html.haml
  24. +19 −0 app/views/devise/shared/_links.haml
  25. +9 −0 app/views/devise/unlocks/new.html.haml
  26. +0 −14 app/views/layouts/application.html.erb
  27. +11 −0 app/views/layouts/application.html.haml
  28. 0 app/views/oauth/access_token.html.haml
  29. 0 app/views/oauth/authorize.html.haml
  30. +2 −0 app/views/public/index.html.haml
  31. +7 −1 config/application.rb
  32. +3 −0 config/environments/development.rb
  33. +2 −0 config/environments/test.rb
  34. +211 −0 config/initializers/devise.rb
  35. +58 −0 config/locales/devise.en.yml
  36. +4 −1 config/locales/en.yml
  37. +6 −0 config/routes.rb
  38. +10 −0 features/oauth/authenticate_client.feature
  39. +9 −0 features/oauth/create_client_application.feature
  40. +25 −0 features/step_definitions/oauth_steps.rb
  41. +96 −0 features/step_definitions/user_steps.rb
  42. +43 −0 features/support/api.rb
  43. +16 −0 features/users/authenticate_user.feature
  44. +21 −0 features/users/create_user.feature
  45. +0 −241 public/index.html
  46. +35 −0 spec/controllers/client_applications_controller_spec.rb
  47. +62 −0 spec/controllers/oauth_controller_spec.rb
  48. +12 −0 spec/controllers/public_controller_spec.rb
  49. +16 −0 spec/factories.rb
  50. +19 −0 spec/models/access_grant_spec.rb
  51. +29 −0 spec/models/client_application_spec.rb
  52. +33 −0 spec/models/user_spec.rb
  53. +1 −4 spec/spec_helper.rb
  54. +3 −0 spec/support/devise.rb
  55. +6 −0 spec/support/mongoid.rb
View
37 Gemfile
@@ -4,12 +4,12 @@ HOST_OS = RbConfig::CONFIG['host_os']
source 'http://rubygems.org'
gem 'rails', '3.1.1'
+# Database
+gem "bson_ext", ">= 1.4.0"
+gem "mongoid", ">= 2.3.0"
# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'
-
-
-
# Gems used only for assets and not required
# in production environments by default.
group :assets do
@@ -17,34 +17,29 @@ group :assets do
gem 'coffee-rails', '~> 3.1.1'
gem 'uglifier', '>= 1.0.3'
end
-
gem 'jquery-rails'
-# To use ActiveModel has_secure_password
-# gem 'bcrypt-ruby', '~> 3.0.0'
-
-# Use unicorn as the web server
-# gem 'unicorn'
-
-# Deploy with Capistrano
-# gem 'capistrano'
-
-# To use debugger
-# gem 'ruby-debug19', :require => 'ruby-debug'
-
# install a Javascript runtime for linux
if HOST_OS =~ /linux/i
gem 'therubyracer', '>= 0.8.2'
end
-
+# Front end goodies
gem "haml", ">= 3.1.2"
gem "haml-rails", ">= 0.3.4", :group => :development
+gem "rails-footnotes", ">= 3.7", :group => :development
+
+# Tools & libs
+gem "devise", ">= 1.4.7"
+
+# Testing & Development with guard
gem "rspec-rails", ">= 2.6.1", :group => [:development, :test]
-#gem "database_cleaner", ">= 0.6.7", :group => :test
-gem "mongoid-rspec", ">= 1.4.4", :group => :test
gem "cucumber-rails", ">= 1.1.1", :group => :test
+gem "accept_values_for", ">= 0.4.3", :group => :test
+gem 'mongoid-rspec', ">= 1.4.4", :group => :test
+gem "factory_girl_rails", ">= 1.3.0", :group => :test
gem "capybara", ">= 1.1.1", :group => :test
gem "launchy", ">= 2.0.5", :group => :test
+# guard
gem "guard", ">= 0.6.2", :group => :development
case HOST_OS
when /darwin/i
@@ -63,7 +58,3 @@ gem "guard-rails", ">= 0.0.3", :group => :development
gem "guard-livereload", ">= 0.3.0", :group => :development
gem "guard-rspec", ">= 0.4.3", :group => :development
gem "guard-cucumber", ">= 0.6.1", :group => :development
-gem "bson_ext", ">= 1.4.0"
-gem "mongoid", ">= 2.3.0"
-gem "devise", ">= 1.4.7"
-gem "rails-footnotes", ">= 3.7", :group => :development
View
41 Gemfile.lock
@@ -1,6 +1,9 @@
GEM
remote: http://rubygems.org/
specs:
+ accept_values_for (0.4.3)
+ activemodel (>= 3.0.0)
+ rspec
actionmailer (3.1.1)
actionpack (= 3.1.1)
mail (~> 2.3.0)
@@ -35,7 +38,7 @@ GEM
bson (1.4.0)
bson_ext (1.4.0)
builder (3.0.0)
- capybara (1.1.1)
+ capybara (1.1.2)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
@@ -51,20 +54,20 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.1.3)
- cucumber (1.1.2)
+ cucumber (1.1.3)
builder (>= 2.1.2)
diff-lcs (>= 1.1.2)
- gherkin (~> 2.6.2)
+ gherkin (~> 2.6.7)
json (>= 1.4.6)
term-ansicolor (>= 1.0.6)
cucumber-rails (1.2.0)
capybara (>= 1.1.1)
cucumber (>= 1.1.1)
nokogiri (>= 1.5.0)
- devise (1.4.9)
+ devise (1.5.1)
bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.0.3)
- warden (~> 1.0.3)
+ warden (~> 1.1)
diff-lcs (1.1.3)
em-websocket (0.3.5)
addressable (>= 2.1.1)
@@ -73,8 +76,13 @@ GEM
eventmachine (0.12.10)
execjs (1.2.9)
multi_json (~> 1.0)
- ffi (1.0.10)
- gherkin (2.6.3)
+ factory_girl (2.3.0)
+ activesupport
+ factory_girl_rails (1.4.0)
+ factory_girl (~> 2.3.0)
+ railties (>= 3.0.0)
+ ffi (1.0.11)
+ gherkin (2.6.7)
json (>= 1.4.6)
guard (0.8.8)
thor (~> 0.14.6)
@@ -90,7 +98,7 @@ GEM
multi_json (~> 1.0.3)
guard-rails (0.0.3)
guard (>= 0.2.2)
- guard-rspec (0.5.3)
+ guard-rspec (0.5.5)
guard (>= 0.8.4)
haml (3.1.3)
haml-rails (0.3.4)
@@ -100,7 +108,7 @@ GEM
railties (~> 3.0)
hike (1.2.1)
i18n (0.6.0)
- jquery-rails (1.0.17)
+ jquery-rails (1.0.18)
railties (~> 3.0)
thor (~> 0.14)
json (1.6.1)
@@ -108,7 +116,7 @@ GEM
launchy (2.0.5)
addressable (~> 2.2.6)
libnotify (0.5.9)
- libv8 (3.3.10.2)
+ libv8 (3.3.10.4)
mail (2.3.0)
i18n (>= 0.4.0)
mime-types (~> 1.16)
@@ -173,13 +181,12 @@ GEM
rspec (~> 2.7.0)
rubyzip (0.9.4)
sass (3.1.10)
- sass-rails (3.1.4)
+ sass-rails (3.1.5)
actionpack (~> 3.1.0)
railties (~> 3.1.0)
- sass (>= 3.1.4)
- sprockets (~> 2.0.0)
+ sass (~> 3.1.10)
tilt (~> 1.3.2)
- selenium-webdriver (2.12.1)
+ selenium-webdriver (2.13.0)
childprocess (>= 0.2.1)
ffi (~> 1.0.9)
json_pure
@@ -197,10 +204,10 @@ GEM
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.31)
- uglifier (1.0.4)
+ uglifier (1.1.0)
execjs (>= 0.3.0)
multi_json (>= 1.0.2)
- warden (1.0.6)
+ warden (1.1.0)
rack (>= 1.0)
xpath (0.1.4)
nokogiri (~> 1.3)
@@ -209,11 +216,13 @@ PLATFORMS
ruby
DEPENDENCIES
+ accept_values_for (>= 0.4.3)
bson_ext (>= 1.4.0)
capybara (>= 1.1.1)
coffee-rails (~> 3.1.1)
cucumber-rails (>= 1.1.1)
devise (>= 1.4.7)
+ factory_girl_rails (>= 1.3.0)
guard (>= 0.6.2)
guard-bundler (>= 0.1.3)
guard-cucumber (>= 0.6.1)
View
28 Guardfile
@@ -0,0 +1,28 @@
+# A sample Guardfile
+# More info at https://github.com/guard/guard#readme
+
+guard 'bundler' do
+ watch('Gemfile')
+end
+
+guard 'cucumber' do
+ watch(%r{^features/.+\.feature$})
+ watch(%r{^features/support/.+$}) { 'features' }
+ watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'features' }
+end
+
+guard 'rspec', :version => 2 do
+ watch(%r{^spec/.+_spec\.rb$})
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
+ watch('spec/spec_helper.rb') { "spec" }
+
+ # Rails example
+ watch(%r{^spec/.+_spec\.rb$})
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
+ watch('spec/spec_helper.rb') { "spec" }
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
+end
+
View
3 app/assets/javascripts/public.js.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
View
3 app/assets/stylesheets/public.css.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the public controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
View
12 app/controllers/client_applications_controller.rb
@@ -0,0 +1,12 @@
+class ClientApplicationsController < ApplicationController
+ def new
+ @client_application = ClientApplication.new
+ end
+ def create
+ @client_application = ClientApplication.create(params[:client_application])
+ redirect_to @client_application
+ end
+ def show
+ @client_application = ClientApplication.find(params[:id])
+ end
+end
View
25 app/controllers/oauth_controller.rb
@@ -0,0 +1,25 @@
+class OauthController < ApplicationController
+ before_filter :authenticate_user!, :except => [:access_token]
+
+ def authorize
+ client_application = ClientApplication.with_id(params[:client_id])
+ @access_grant = AccessGrant.create(:client_application => client_application, :user => current_user)
+ redirect_to "#{params[:redirect_uri]}&code=#{@access_grant.code}&response_type=code"
+ end
+
+ def access_token
+ client_application = ClientApplication.authorize(params[:client_id], params[:client_secret])
+ if client_application.nil?
+ render :json => {:error => "No client application found"}.to_json
+ return
+ end
+
+ access_grant = AccessGrant.authorize(client_application, params[:code])
+ if access_grant.nil?
+ render :json => {:error => "No grant found"}.to_json
+ return
+ end
+
+ render :json => {:access_token => access_grant.access_token}.to_json
+ end
+end
View
5 app/controllers/public_controller.rb
@@ -0,0 +1,5 @@
+class PublicController < ApplicationController
+ def index
+ end
+
+end
View
2 app/helpers/public_helper.rb
@@ -0,0 +1,2 @@
+module PublicHelper
+end
View
18 app/models/access_grant.rb
@@ -0,0 +1,18 @@
+class AccessGrant
+ include Mongoid::Document
+ belongs_to :user
+ belongs_to :client_application
+ field :code, type: String
+ field :access_token, type: String
+
+ before_create :generate_tokens
+
+ def self.authorize client_application, code
+ where(client_application_id: client_application.id).and(code: code).last
+ end
+
+ protected
+ def generate_tokens
+ self.code, self.access_token = SecureRandom.hex(16), SecureRandom.hex(16)
+ end
+end
View
31 app/models/client_application.rb
@@ -0,0 +1,31 @@
+
+class ClientApplication
+ include Mongoid::Document
+ field :name, :type => String
+ field :description, :type => String
+ field :key, :type => String
+ field :secret, :type => String
+
+ before_create :generate_keys
+
+ validates :name, :presence => true,
+ :length => {:minimum => 2}
+
+ attr_accessible :name, :description
+
+ def self.with_id client_id
+ where(key: client_id).last
+ end
+
+ def self.authorize client_id, client_secret
+ where(key: client_id).and(secret: client_secret).last
+ end
+
+ protected
+
+ def generate_keys
+ self.key = SecureRandom.base64(40).gsub(/\W/, '')[0,40]
+ self.secret = SecureRandom.base64(40).gsub(/\W/, '')[0,40]
+ end
+
+end
View
29 app/models/user.rb
@@ -0,0 +1,29 @@
+class User
+ include Mongoid::Document
+ # Include default devise modules. Others available are:
+ # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
+ devise :database_authenticatable, :token_authenticatable, :registerable,
+ :recoverable, :rememberable, :trackable, :validatable
+
+ field :email, :type => String
+ field :username, :type => String
+ validates :username, :presence => true,
+ :length => {:minimum => 2},
+ :format => { :with => /^[A-Za-z\d_]+$/, :message => "can only be alphanumeric with no spaces"},
+ :uniqueness => true
+
+ # Virtual login for authenticating via username or email
+ attr_accessor :login
+ attr_accessible :login, :username, :email, :password, :password_confirmation, :remember_me
+
+ def self.find_for_database_authentication(conditions)
+ login = conditions.delete(:login)
+ self.any_of({ :username => login }, { :email => login }).first
+ end
+
+ self.token_authentication_key = "access_token"
+ def self.find_for_token_authentication conditions
+ find(AccessGrant.where(access_token: conditions[token_authentication_key]).only(:user_id).last.user_id)
+ end
+
+end
View
12 app/views/client_applications/new.html.haml
@@ -0,0 +1,12 @@
+%h2 New Client Application
+
+= form_for @client_application do |f|
+ %div
+ = f.label :name
+ %br/
+ = f.text_field :name
+ %div
+ = f.label :description
+ %br/
+ = f.text_field :description
+ %div= f.submit "Create"
View
5 app/views/client_applications/show.html.haml
@@ -0,0 +1,5 @@
+%ul#client_application
+ %li= @client_application.name
+ %li= @client_application.description
+ %li#application_key= @client_application.key
+ %li#application_secret= @client_application.secret
View
9 app/views/devise/confirmations/new.html.haml
@@ -0,0 +1,9 @@
+%h2 Resend confirmation instructions
+= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f|
+ = devise_error_messages!
+ %div
+ = f.label :email
+ %br/
+ = f.email_field :email
+ %div= f.submit "Resend confirmation instructions"
+= render :partial => "devise/shared/links"
View
4 app/views/devise/mailer/confirmation_instructions.html.haml
@@ -0,0 +1,4 @@
+%p
+ Welcome #{@resource.email}!
+%p You can confirm your account through the link below:
+%p= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token)
View
6 app/views/devise/mailer/reset_password_instructions.html.haml
@@ -0,0 +1,6 @@
+%p
+ Hello #{@resource.email}!
+%p Someone has requested a link to change your password, and you can do this through the link below.
+%p= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token)
+%p If you didn't request this, please ignore this email.
+%p Your password won't change until you access the link above and create a new one.
View
5 app/views/devise/mailer/unlock_instructions.html.haml
@@ -0,0 +1,5 @@
+%p
+ Hello #{@resource.email}!
+%p Your account has been locked due to an excessive amount of unsuccessful sign in attempts.
+%p Click the link below to unlock your account:
+%p= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token)
View
14 app/views/devise/passwords/edit.html.haml
@@ -0,0 +1,14 @@
+%h2 Change your password
+= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f|
+ = devise_error_messages!
+ = f.hidden_field :reset_password_token
+ %div
+ = f.label :password, "New password"
+ %br/
+ = f.password_field :password
+ %div
+ = f.label :password_confirmation, "Confirm new password"
+ %br/
+ = f.password_field :password_confirmation
+ %div= f.submit "Change my password"
+= render :partial => "devise/shared/links"
View
9 app/views/devise/passwords/new.html.haml
@@ -0,0 +1,9 @@
+%h2 Forgot your password?
+= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f|
+ = devise_error_messages!
+ %div
+ = f.label :email
+ %br/
+ = f.email_field :email
+ %div= f.submit "Send me reset password instructions"
+= render :partial => "devise/shared/links"
View
31 app/views/devise/registrations/edit.html.haml
@@ -0,0 +1,31 @@
+%h2
+ Edit #{resource_name.to_s.humanize}
+= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f|
+ = devise_error_messages!
+ %div
+ = f.label :username
+ %br/
+ = f.text_field :username
+ %div
+ = f.label :email
+ %br/
+ = f.email_field :email
+ %div
+ = f.label :password
+ %i (leave blank if you don't want to change it)
+ %br/
+ = f.password_field :password
+ %div
+ = f.label :password_confirmation
+ %br/
+ = f.password_field :password_confirmation
+ %div
+ = f.label :current_password
+ %i (we need your current password to confirm your changes)
+ %br/
+ = f.password_field :current_password
+ %div= f.submit "Update"
+%h3 Cancel my account
+%p
+ Unhappy? #{link_to "Cancel my account", registration_path(resource_name), :confirm => "Are you sure?", :method => :delete}.
+= link_to "Back", :back
View
21 app/views/devise/registrations/new.html.haml
@@ -0,0 +1,21 @@
+%h2 Sign up
+= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f|
+ = devise_error_messages!
+ %div
+ = f.label :username
+ %br/
+ = f.text_field :username
+ %div
+ = f.label :email
+ %br/
+ = f.email_field :email
+ %div
+ = f.label :password
+ %br/
+ = f.password_field :password
+ %div
+ = f.label :password_confirmation
+ %br/
+ = f.password_field :password_confirmation
+ %div= f.submit "Sign up"
+= render :partial => "devise/shared/links"
View
16 app/views/devise/sessions/new.html.haml
@@ -0,0 +1,16 @@
+%h2 Sign in
+= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f|
+ %div
+ = f.label :login
+ %br/
+ = f.text_field :login
+ %div
+ = f.label :password
+ %br/
+ = f.password_field :password
+ - if devise_mapping.rememberable?
+ %div
+ = f.check_box :remember_me
+ = f.label :remember_me
+ %div= f.submit "Sign in"
+= render :partial => "devise/shared/links"
View
19 app/views/devise/shared/_links.haml
@@ -0,0 +1,19 @@
+- if controller_name != 'sessions'
+ = link_to "Sign in", new_session_path(resource_name)
+ %br/
+- if devise_mapping.registerable? && controller_name != 'registrations'
+ = link_to "Sign up", new_registration_path(resource_name)
+ %br/
+- if devise_mapping.recoverable? && controller_name != 'passwords'
+ = link_to "Forgot your password?", new_password_path(resource_name)
+ %br/
+- if devise_mapping.confirmable? && controller_name != 'confirmations'
+ = link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name)
+ %br/
+- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks'
+ = link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name)
+ %br/
+- if devise_mapping.omniauthable?
+ - resource_class.omniauth_providers.each do |provider|
+ = link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider)
+ %br/
View
9 app/views/devise/unlocks/new.html.haml
@@ -0,0 +1,9 @@
+%h2 Resend unlock instructions
+= form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f|
+ = devise_error_messages!
+ %div
+ = f.label :email
+ %br/
+ = f.email_field :email
+ %div= f.submit "Resend unlock instructions"
+= render :partial => "devise/shared/links"
View
14 app/views/layouts/application.html.erb
@@ -1,14 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
- <title>Markedli</title>
- <%= stylesheet_link_tag "application" %>
- <%= javascript_include_tag "application" %>
- <%= csrf_meta_tags %>
-</head>
-<body>
-
-<%= yield %>
-
-</body>
-</html>
View
11 app/views/layouts/application.html.haml
@@ -0,0 +1,11 @@
+!!!
+%html
+ %head
+ %title Markedli
+ = stylesheet_link_tag "application"
+ = javascript_include_tag "application"
+ = csrf_meta_tags
+ %body
+ - flash.each do |name, msg|
+ = content_tag :div, msg, :id => "flash_#{name}" if msg.is_a?(String)
+ = yield
View
0 app/views/oauth/access_token.html.haml
No changes.
View
0 app/views/oauth/authorize.html.haml
No changes.
View
2 app/views/public/index.html.haml
@@ -0,0 +1,2 @@
+%h1 Public#index
+%p Find me in app/views/public/index.html.haml
View
8 config/application.rb
@@ -43,12 +43,18 @@ class Application < Rails::Application
config.encoding = "utf-8"
# Configure sensitive parameters which will be filtered from the log file.
- config.filter_parameters += [:password]
+ config.filter_parameters += [:password, :password_confirmation]
# Enable the asset pipeline
config.assets.enabled = true
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
+
+ # Configure rspec generators to not generate view & helper specs
+ config.generators do |g|
+ g.view_specs false
+ g.helper_specs false
+ end
end
end
View
3 config/environments/development.rb
@@ -27,4 +27,7 @@
# Expands the lines which load the assets
config.assets.debug = true
+
+ # Config mailer for devise
+ config.action_mailer.default_url_options = { :host => 'localhost:3000' }
end
View
2 config/environments/test.rb
@@ -28,6 +28,8 @@
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
+ # Config ActionMailer for devise
+ config.action_mailer.default_url_options = { :host => 'localhost:3000' }
# Use SQL instead of Active Record's schema dumper when creating the test database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
View
211 config/initializers/devise.rb
@@ -0,0 +1,211 @@
+# Use this hook to configure devise mailer, warden hooks and so forth. The first
+# four configuration values can also be set straight in your models.
+Devise.setup do |config|
+ # ==> Mailer Configuration
+ # Configure the e-mail address which will be shown in Devise::Mailer,
+ # note that it will be overwritten if you use your own mailer class with default "from" parameter.
+ config.mailer_sender = "system@marked.li"
+
+ # Configure the class responsible to send e-mails.
+ # config.mailer = "Devise::Mailer"
+
+ # ==> ORM configuration
+ # Load and configure the ORM. Supports :active_record (default) and
+ # :mongoid (bson_ext recommended) by default. Other ORMs may be
+ # available as additional gems.
+ require 'devise/orm/mongoid'
+
+ # ==> Configuration for any authentication mechanism
+ # Configure which keys are used when authenticating a user. The default is
+ # just :email. You can configure it to use [:username, :subdomain], so for
+ # authenticating a user, both parameters are required. Remember that those
+ # parameters are used only when authenticating and not when retrieving from
+ # session. If you need permissions, you should implement that in a before filter.
+ # You can also supply a hash where the value is a boolean determining whether
+ # or not authentication should be aborted when the value is not present.
+ config.authentication_keys = [ :login ]
+
+ # Configure parameters from the request object used for authentication. Each entry
+ # given should be a request method and it will automatically be passed to the
+ # find_for_authentication method and considered in your model lookup. For instance,
+ # if you set :request_keys to [:subdomain], :subdomain will be used on authentication.
+ # The same considerations mentioned for authentication_keys also apply to request_keys.
+ # config.request_keys = []
+
+ # Configure which authentication keys should be case-insensitive.
+ # These keys will be downcased upon creating or modifying a user and when used
+ # to authenticate or find a user. Default is :email.
+ config.case_insensitive_keys = [ :email ]
+
+ # Configure which authentication keys should have whitespace stripped.
+ # These keys will have whitespace before and after removed upon creating or
+ # modifying a user and when used to authenticate or find a user. Default is :email.
+ config.strip_whitespace_keys = [ :email, :username ]
+
+ # Tell if authentication through request.params is enabled. True by default.
+ # config.params_authenticatable = true
+
+ # Tell if authentication through HTTP Basic Auth is enabled. False by default.
+ # config.http_authenticatable = false
+
+ # If http headers should be returned for AJAX requests. True by default.
+ # config.http_authenticatable_on_xhr = true
+
+ # The realm used in Http Basic Authentication. "Application" by default.
+ # config.http_authentication_realm = "Application"
+
+ # It will change confirmation, password recovery and other workflows
+ # to behave the same regardless if the e-mail provided was right or wrong.
+ # Does not affect registerable.
+ # config.paranoid = true
+
+ # ==> Configuration for :database_authenticatable
+ # For bcrypt, this is the cost for hashing the password and defaults to 10. If
+ # using other encryptors, it sets how many times you want the password re-encrypted.
+ #
+ # Limiting the stretches to just one in testing will increase the performance of
+ # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use
+ # a value less than 10 in other environments.
+ config.stretches = Rails.env.test? ? 1 : 10
+
+ # Setup a pepper to generate the encrypted password.
+ # config.pepper = "dbd263382fa37b60402d0f4ea86019a483db04807a056e5b2fb27bc6621004549aca78ebef022a815faa07c42add923187b4355e0d80a5f9b67983f8b20122f8"
+
+ # ==> Configuration for :confirmable
+ # A period that the user is allowed to access the website even without
+ # confirming his account. For instance, if set to 2.days, the user will be
+ # able to access the website for two days without confirming his account,
+ # access will be blocked just in the third day. Default is 0.days, meaning
+ # the user cannot access the website without confirming his account.
+ # config.confirm_within = 2.days
+
+ # Defines which key will be used when confirming an account
+ # config.confirmation_keys = [ :email ]
+
+ # ==> Configuration for :rememberable
+ # The time the user will be remembered without asking for credentials again.
+ # config.remember_for = 2.weeks
+
+ # If true, a valid remember token can be re-used between multiple browsers.
+ # config.remember_across_browsers = true
+
+ # If true, extends the user's remember period when remembered via cookie.
+ # config.extend_remember_period = false
+
+ # If true, uses the password salt as remember token. This should be turned
+ # to false if you are not using database authenticatable.
+ config.use_salt_as_remember_token = true
+
+ # Options to be passed to the created cookie. For instance, you can set
+ # :secure => true in order to force SSL only cookies.
+ # config.cookie_options = {}
+
+ # ==> Configuration for :validatable
+ # Range for password length. Default is 6..128.
+ # config.password_length = 6..128
+
+ # Email regex used to validate email formats. It simply asserts that
+ # an one (and only one) @ exists in the given string. This is mainly
+ # to give user feedback and not to assert the e-mail validity.
+ # config.email_regexp = /\A[^@]+@[^@]+\z/
+
+ # ==> Configuration for :timeoutable
+ # The time you want to timeout the user session without activity. After this
+ # time the user will be asked for credentials again. Default is 30 minutes.
+ # config.timeout_in = 30.minutes
+
+ # ==> Configuration for :lockable
+ # Defines which strategy will be used to lock an account.
+ # :failed_attempts = Locks an account after a number of failed attempts to sign in.
+ # :none = No lock strategy. You should handle locking by yourself.
+ # config.lock_strategy = :failed_attempts
+
+ # Defines which key will be used when locking and unlocking an account
+ # config.unlock_keys = [ :email ]
+
+ # Defines which strategy will be used to unlock an account.
+ # :email = Sends an unlock link to the user email
+ # :time = Re-enables login after a certain amount of time (see :unlock_in below)
+ # :both = Enables both strategies
+ # :none = No unlock strategy. You should handle unlocking by yourself.
+ # config.unlock_strategy = :both
+
+ # Number of authentication tries before locking an account if lock_strategy
+ # is failed attempts.
+ # config.maximum_attempts = 20
+
+ # Time interval to unlock the account if :time is enabled as unlock_strategy.
+ # config.unlock_in = 1.hour
+
+ # ==> Configuration for :recoverable
+ #
+ # Defines which key will be used when recovering the password for an account
+ # config.reset_password_keys = [ :email ]
+
+ # Time interval you can reset your password with a reset password key.
+ # Don't put a too small interval or your users won't have the time to
+ # change their passwords.
+ config.reset_password_within = 2.hours
+
+ # ==> Configuration for :encryptable
+ # Allow you to use another encryption algorithm besides bcrypt (default). You can use
+ # :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1,
+ # :authlogic_sha512 (then you should set stretches above to 20 for default behavior)
+ # and :restful_authentication_sha1 (then you should set stretches to 10, and copy
+ # REST_AUTH_SITE_KEY to pepper)
+ # config.encryptor = :sha512
+
+ # ==> Configuration for :token_authenticatable
+ # Defines name of the authentication token params key
+ # config.token_authentication_key = :auth_token
+
+ # If true, authentication through token does not store user in session and needs
+ # to be supplied on each request. Useful if you are using the token as API token.
+ # config.stateless_token = false
+
+ # ==> Scopes configuration
+ # Turn scoped views on. Before rendering "sessions/new", it will first check for
+ # "users/sessions/new". It's turned off by default because it's slower if you
+ # are using only default views.
+ # config.scoped_views = false
+
+ # Configure the default scope given to Warden. By default it's the first
+ # devise role declared in your routes (usually :user).
+ # config.default_scope = :user
+
+ # Configure sign_out behavior.
+ # Sign_out action can be scoped (i.e. /users/sign_out affects only :user scope).
+ # The default is true, which means any logout action will sign out all active scopes.
+ # config.sign_out_all_scopes = true
+
+ # ==> Navigation configuration
+ # Lists the formats that should be treated as navigational. Formats like
+ # :html, should redirect to the sign in page when the user does not have
+ # access, but formats like :xml or :json, should return 401.
+ #
+ # If you have any extra navigational formats, like :iphone or :mobile, you
+ # should add them to the navigational formats lists.
+ #
+ # The :"*/*" and "*/*" formats below is required to match Internet
+ # Explorer requests.
+ # config.navigational_formats = [:"*/*", "*/*", :html]
+
+ # The default HTTP method used to sign out a resource. Default is :delete.
+ # config.sign_out_via = :delete
+ config.sign_out_via = Rails.env.test? ? :get : :delete
+
+ # ==> OmniAuth
+ # Add a new OmniAuth provider. Check the wiki for more information on setting
+ # up on your models and hooks.
+ # config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo'
+
+ # ==> Warden configuration
+ # If you want to use other strategies, that are not supported by Devise, or
+ # change the failure app, you can configure them inside the config.warden block.
+ #
+ # config.warden do |manager|
+ # manager.failure_app = AnotherApp
+ # manager.intercept_401 = false
+ # manager.default_strategies(:scope => :user).unshift :some_external_strategy
+ # end
+end
View
58 config/locales/devise.en.yml
@@ -0,0 +1,58 @@
+# Additional translations at http://github.com/plataformatec/devise/wiki/I18n
+
+en:
+ errors:
+ messages:
+ expired: "has expired, please request a new one"
+ not_found: "not found"
+ already_confirmed: "was already confirmed, please try signing in"
+ not_locked: "was not locked"
+ not_saved:
+ one: "1 error prohibited this %{resource} from being saved:"
+ other: "%{count} errors prohibited this %{resource} from being saved:"
+
+ devise:
+ failure:
+ already_authenticated: 'You are already signed in.'
+ unauthenticated: 'You need to sign in or sign up before continuing.'
+ unconfirmed: 'You have to confirm your account before continuing.'
+ locked: 'Your account is locked.'
+ invalid: 'Invalid email or password.'
+ invalid_token: 'Invalid authentication token.'
+ timeout: 'Your session expired, please sign in again to continue.'
+ inactive: 'Your account was not activated yet.'
+ sessions:
+ signed_in: 'Signed in successfully.'
+ signed_out: 'Signed out successfully.'
+ passwords:
+ send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.'
+ updated: 'Your password was changed successfully. You are now signed in.'
+ updated_not_active: 'Your password was changed successfully.'
+ send_paranoid_instructions: "If your e-mail exists on our database, you will receive a password recovery link on your e-mail"
+ confirmations:
+ send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.'
+ send_paranoid_instructions: 'If your e-mail exists on our database, you will receive an email with instructions about how to confirm your account in a few minutes.'
+ confirmed: 'Your account was successfully confirmed. You are now signed in.'
+ registrations:
+ signed_up: 'Welcome! You have signed up successfully.'
+ inactive_signed_up: 'You have signed up successfully. However, we could not sign you in because your account is %{reason}.'
+ updated: 'You updated your account successfully.'
+ destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.'
+ reasons:
+ inactive: 'inactive'
+ unconfirmed: 'unconfirmed'
+ locked: 'locked'
+ unlocks:
+ send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.'
+ unlocked: 'Your account was successfully unlocked. You are now signed in.'
+ send_paranoid_instructions: 'If your account exists, you will receive an email with instructions about how to unlock it in a few minutes.'
+ omniauth_callbacks:
+ success: 'Successfully authorized from %{kind} account.'
+ failure: 'Could not authorize you from %{kind} because "%{reason}".'
+ mailer:
+ confirmation_instructions:
+ subject: 'Confirmation instructions'
+ reset_password_instructions:
+ subject: 'Reset password instructions'
+ unlock_instructions:
+ subject: 'Unlock Instructions'
View
5 config/locales/en.yml
@@ -2,4 +2,7 @@
# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
en:
- hello: "Hello world"
+ mongoid:
+ attributes:
+ user:
+ login: "Username or email"
View
6 config/routes.rb
@@ -1,4 +1,10 @@
Markedli::Application.routes.draw do
+ match 'oauth/authorize' => 'oauth#authorize'
+ match 'oauth/access_token' => 'oauth#access_token'
+
+ resources :client_applications, :only => [:new, :create, :show]
+ devise_for :users
+ root :to => "public#index"
# The priority is based upon order of creation:
# first created -> highest priority.
View
10 features/oauth/authenticate_client.feature
@@ -0,0 +1,10 @@
+Feature: Client application authentication
+ In order to integrate data from markedli into other applications
+ As a user
+ I want designated client applications to authenticate
+
+ Scenario: Oauth Authentication
+ Given I am logged in
+ And I have created a client application
+ When My client application authenticates
+ Then My client has an access token
View
9 features/oauth/create_client_application.feature
@@ -0,0 +1,9 @@
+Feature: User creates client application
+ In order to integrate my bookmarks to other applicatons
+ As a user
+ I want to create client applications for markedly to interact with
+
+ Scenario: User creates a client application
+ Given I am logged in
+ When I create a client application
+ Then I have a key and secret for the application
View
25 features/step_definitions/oauth_steps.rb
@@ -0,0 +1,25 @@
+Given /^I have created a client application$/ do
+ @application_key, @application_secret = new_client_app
+end
+
+When /^I create a client application$/ do
+ new_client_app
+end
+
+When /^My client application authenticates$/ do
+ #get user authorization and temp code
+ @grant_code = authorize_client @application_key
+ @grant_code.should_not be_nil
+ @grant_code.should_not be_empty
+end
+
+Then /^My client has an access token$/ do
+ access_token = exchange_code_for_token @application_key, @application_secret, @grant_code
+ access_token.should_not be_nil
+ access_token.should_not be_empty
+end
+
+Then /^I have a key and secret for the application$/ do
+ find('li#application_key').text.should_not be_empty
+ find('li#application_secret').text.should_not be_empty
+end
View
96 features/step_definitions/user_steps.rb
@@ -0,0 +1,96 @@
+Given /^I have a user account$/ do
+ @user = Factory(:user)
+end
+
+Given /^A user account exists$/ do
+ @user = Factory(:user)
+end
+
+Given /^I am logged out$/ do
+ visit '/users/sign_out'
+end
+
+Given /^I am a guest$/ do
+ visit '/users/sign_out'
+end
+
+Given /^I am logged in$/ do
+ @user = Factory(:user)
+ visit '/users/sign_in'
+
+ fill_in 'Username or email', :with => @user.username
+ fill_in 'Password', :with => @user.password
+
+ click_button "Sign in"
+end
+
+When /^I create a user account$/ do
+ visit '/users/sign_up'
+
+ fill_in 'Username', :with => "testing"
+ fill_in 'Email', :with => "testing@test.com"
+ fill_in 'Password', :with => "simplepass"
+ fill_in 'Password confirmation', :with => "simplepass"
+
+ click_button "Sign up"
+end
+
+When /^I create a user account with a taken email$/ do
+ visit '/users/sign_up'
+
+ fill_in 'Username', :with => "testing"
+ fill_in 'Email', :with => @user.email
+ fill_in 'Password', :with => "newpass"
+ fill_in 'Password confirmation', :with => "newpass"
+
+ click_button "Sign up"
+end
+
+When /^I create a user account with a taken username$/ do
+ visit '/users/sign_up'
+
+ fill_in 'Username', :with => @user.username
+ fill_in 'Email', :with => "someother@email.com"
+ fill_in 'Password', :with => "newpass"
+ fill_in 'Password confirmation', :with => "newpass"
+
+ click_button "Sign up"
+end
+When /^I sign in using my email$/ do
+ visit '/users/sign_in'
+
+ fill_in 'Username or email', :with => @user.email
+ fill_in 'Password', :with => @user.password
+
+ click_button "Sign in"
+end
+
+When /^I sign in using my username$/ do
+ visit '/users/sign_in'
+
+ fill_in 'Username or email', :with => @user.username
+ fill_in 'Password', :with => @user.password
+
+ click_button "Sign in"
+end
+
+When /^I sign in$/ do
+ visit '/users/sign_in'
+
+ fill_in 'Username or email', :with => @user.username
+ fill_in 'Password', :with => @user.password
+
+ click_button "Sign in"
+end
+
+Then /^I should be notified of my new account$/ do
+ find('#flash_notice').should have_content('You have signed up successfully')
+end
+
+Then /^I am signed in$/ do
+ find('#flash_notice').should have_content('Signed in successfully.')
+end
+
+Then /^I should be notified of an error$/ do
+ find('#error_explanation').should have_content('prohibited this user from being saved')
+end
View
43 features/support/api.rb
@@ -0,0 +1,43 @@
+module CapybaraApp
+ def app
+ Capybara.app
+ end
+ def session
+ puts page.driver.response.methods
+ page.driver.response['rack.session']
+ end
+end
+module ApiHelpers
+ def get_param_from_response response, param
+ Rack::Utils.parse_nested_query(response["Location"])[param]
+ end
+ def new_client_app
+ attributes = Factory.attributes_for(:client_application)
+
+ visit '/client_applications/new'
+ fill_in 'Name', :with => attributes[:name]
+ fill_in 'Description', :with => attributes[:description]
+ click_button 'Create'
+
+ page.should have_content attributes[:name]
+ return find('#application_key').text, find('#application_secret').text
+ end
+ def authorize_client key
+ page.driver.get "/oauth/authorize?response_type=code&client_id=#{key}&state=test"
+ page.driver.response.should be_redirect
+ get_param_from_response page.driver.response, "code"
+ end
+ def exchange_code_for_token key, secret, code
+ get "/oauth/access_token?client_id=#{key}&client_secret=#{secret}&code=#{code}"
+ last_response.should be_ok
+ last_response.body["access_token"]
+ end
+ def new_client_token
+ key, secret = new_client_app
+ code = authorize_client key
+ exchange_code_for_token key, secret, code
+ end
+end
+World(ApiHelpers)
+World(CapybaraApp)
+World(Rack::Test::Methods)
View
16 features/users/authenticate_user.feature
@@ -0,0 +1,16 @@
+Feature: Authenticate a user
+ In order to create an orginize bookmarks
+ As a user
+ I want to be able to sign in to my account
+
+ Scenario: User signs in using email
+ Given I have a user account
+ And I am logged out
+ When I sign in using my email
+ Then I am signed in
+
+ Scenario: User signs in using username
+ Given I have a user account
+ And I am logged out
+ When I sign in using my username
+ Then I am signed in
View
21 features/users/create_user.feature
@@ -0,0 +1,21 @@
+Feature: Create a user
+ In order to create and orginize bookmarks
+ As a guest
+ I want to create a user account
+
+ Scenario: Guest creates account
+ Given I am a guest
+ When I create a user account
+ Then I should be notified of my new account
+
+ Scenario: Guest tries to create an account with a taken email
+ Given A user account exists
+ And I am logged out
+ When I create a user account with a taken email
+ Then I should be notified of an error
+
+ Scenario: Guest tries to create an account with a taken username
+ Given A user account exists
+ And I am logged out
+ When I create a user account with a taken username
+ Then I should be notified of an error
View
241 public/index.html
@@ -1,241 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <title>Ruby on Rails: Welcome aboard</title>
- <style type="text/css" media="screen">
- body {
- margin: 0;
- margin-bottom: 25px;
- padding: 0;
- background-color: #f0f0f0;
- font-family: "Lucida Grande", "Bitstream Vera Sans", "Verdana";
- font-size: 13px;
- color: #333;
- }
-
- h1 {
- font-size: 28px;
- color: #000;
- }
-
- a {color: #03c}
- a:hover {
- background-color: #03c;
- color: white;
- text-decoration: none;
- }
-
-
- #page {
- background-color: #f0f0f0;
- width: 750px;
- margin: 0;
- margin-left: auto;
- margin-right: auto;
- }
-
- #content {
- float: left;
- background-color: white;
- border: 3px solid #aaa;
- border-top: none;
- padding: 25px;
- width: 500px;
- }
-
- #sidebar {
- float: right;
- width: 175px;
- }
-
- #footer {
- clear: both;
- }
-
- #header, #about, #getting-started {
- padding-left: 75px;
- padding-right: 30px;
- }
-
-
- #header {
- background-image: url("/assets/rails.png");
- background-repeat: no-repeat;
- background-position: top left;
- height: 64px;
- }
- #header h1, #header h2 {margin: 0}
- #header h2 {
- color: #888;
- font-weight: normal;
- font-size: 16px;
- }
-
-
- #about h3 {
- margin: 0;
- margin-bottom: 10px;
- font-size: 14px;
- }
-
- #about-content {
- background-color: #ffd;
- border: 1px solid #fc0;
- margin-left: -55px;
- margin-right: -10px;
- }
- #about-content table {
- margin-top: 10px;
- margin-bottom: 10px;
- font-size: 11px;
- border-collapse: collapse;
- }
- #about-content td {
- padding: 10px;
- padding-top: 3px;
- padding-bottom: 3px;
- }
- #about-content td.name {color: #555}
- #about-content td.value {color: #000}
-
- #about-content ul {
- padding: 0;
- list-style-type: none;
- }
-
- #about-content.failure {
- background-color: #fcc;
- border: 1px solid #f00;
- }
- #about-content.failure p {
- margin: 0;
- padding: 10px;
- }
-
-
- #getting-started {
- border-top: 1px solid #ccc;
- margin-top: 25px;
- padding-top: 15px;
- }
- #getting-started h1 {
- margin: 0;
- font-size: 20px;
- }
- #getting-started h2 {
- margin: 0;
- font-size: 14px;
- font-weight: normal;
- color: #333;
- margin-bottom: 25px;
- }
- #getting-started ol {
- margin-left: 0;
- padding-left: 0;
- }
- #getting-started li {
- font-size: 18px;
- color: #888;
- margin-bottom: 25px;
- }
- #getting-started li h2 {
- margin: 0;
- font-weight: normal;
- font-size: 18px;
- color: #333;
- }
- #getting-started li p {
- color: #555;
- font-size: 13px;
- }
-
-
- #sidebar ul {
- margin-left: 0;
- padding-left: 0;
- }
- #sidebar ul h3 {
- margin-top: 25px;
- font-size: 16px;
- padding-bottom: 10px;
- border-bottom: 1px solid #ccc;
- }
- #sidebar li {
- list-style-type: none;
- }
- #sidebar ul.links li {
- margin-bottom: 5px;
- }
-
- .filename {
- font-style: italic;
- }
- </style>
- <script type="text/javascript">
- function about() {
- info = document.getElementById('about-content');
- if (window.XMLHttpRequest)
- { xhr = new XMLHttpRequest(); }
- else
- { xhr = new ActiveXObject("Microsoft.XMLHTTP"); }
- xhr.open("GET","rails/info/properties",false);
- xhr.send("");
- info.innerHTML = xhr.responseText;
- info.style.display = 'block'
- }
- </script>
- </head>
- <body>
- <div id="page">
- <div id="sidebar">
- <ul id="sidebar-items">
- <li>
- <h3>Browse the documentation</h3>
- <ul class="links">
- <li><a href="http://guides.rubyonrails.org/">Rails Guides</a></li>
- <li><a href="http://api.rubyonrails.org/">Rails API</a></li>
- <li><a href="http://www.ruby-doc.org/core/">Ruby core</a></li>
- <li><a href="http://www.ruby-doc.org/stdlib/">Ruby standard library</a></li>
- </ul>
- </li>
- </ul>
- </div>
-
- <div id="content">
- <div id="header">
- <h1>Welcome aboard</h1>
- <h2>You&rsquo;re riding Ruby on Rails!</h2>
- </div>
-
- <div id="about">
- <h3><a href="rails/info/properties" onclick="about(); return false">About your application&rsquo;s environment</a></h3>
- <div id="about-content" style="display: none"></div>
- </div>
-
- <div id="getting-started">
- <h1>Getting started</h1>
- <h2>Here&rsquo;s how to get rolling:</h2>
-
- <ol>
- <li>
- <h2>Use <code>rails generate</code> to create your models and controllers</h2>
- <p>To see all available options, run it without parameters.</p>
- </li>
-
- <li>
- <h2>Set up a default route and remove <span class="filename">public/index.html</span></h2>
- <p>Routes are set up in <span class="filename">config/routes.rb</span>.</p>
- </li>
-
- <li>
- <h2>Create your database</h2>
- <p>Run <code>rake db:create</code> to create your database. If you're not using SQLite (the default), edit <span class="filename">config/database.yml</span> with your username and password.</p>
- </li>
- </ol>
- </div>
- </div>
-
- <div id="footer">&nbsp;</div>
- </div>
- </body>
-</html>
View
35 spec/controllers/client_applications_controller_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe ClientApplicationsController do
+ describe "#new" do
+ it "should assign a new client application as @client_application" do
+ get :new
+ assigns(:client_application).should be_a_new(ClientApplication)
+ end
+ end
+ describe "#create" do
+ context "with valid params" do
+ it "creates a new ClientApplication" do
+ expect {
+ post :create, :client_application => Factory.attributes_for(:client_application)
+ }.to change(ClientApplication, :count).by(1)
+ end
+ it "assigns the created ClientApplication as @client_application" do
+ post :create, :client_application => Factory.attributes_for(:client_application)
+ assigns(:client_application).should be_a(ClientApplication)
+ assigns(:client_application).should be_persisted
+ end
+ it "redirects to the newly created ClientApplication" do
+ post :create, :client_application => Factory.attributes_for(:client_application)
+ response.should redirect_to(ClientApplication.last)
+ end
+ end
+ end
+ describe "#show" do
+ it "assigns the requested ClientApplication as @client_application" do
+ client_application = ClientApplication.create! Factory.attributes_for(:client_application)
+ get :show, :id => client_application.id
+ assigns(:client_application).should eq(client_application)
+ end
+ end
+end
View
62 spec/controllers/oauth_controller_spec.rb
@@ -0,0 +1,62 @@
+require 'spec_helper'
+
+describe OauthController do
+ describe '#authorize' do
+ context "without a logged in user" do
+ it "redirects to login" do
+ get :authorize
+ response.should redirect_to new_user_session_path
+ end
+ end
+ context "with a logged in user" do
+ let(:user) { Factory(:user) }
+ before(:each) { sign_in user }
+ let(:client_application) { ClientApplication.create! Factory.attributes_for(:client_application) }
+ let(:client_key) { client_application.key }
+ let(:redirect_uri) { "http://client.app" }
+ it "creates an AccessGrant for the application & logged in user" do
+ expect {
+ get :authorize, :client_id => client_key
+ }.to change(AccessGrant, :count).by(1)
+ assigns(:access_grant).client_application.should == client_application
+ assigns(:access_grant).user.should == user
+ end
+ it "redirects the user back to the client" do
+ get :authorize, :client_id => client_key, :redirect_uri => redirect_uri
+ response.should be_redirect
+ response.redirect_url.should be_include(redirect_uri)
+ end
+ it "returns an access grant code param" do
+ get :authorize, :client_id => client_key, :redirect_uri => redirect_uri
+ response.redirect_url.should be_include("code=#{AccessGrant.last.code}")
+ end
+ end
+ end
+ describe "#access_token" do
+ let(:client_application) { ClientApplication.create! Factory.attributes_for(:client_application) }
+ let(:client_key) { client_application.key }
+ let(:client_secret) { client_application.secret }
+ let(:access_grant) { AccessGrant.create! :client_application => client_application }
+ let(:code) { access_grant.code }
+ it "verifies the client application" do
+ ClientApplication.should_receive(:authorize).with(client_key, client_secret)
+ get :access_token, :client_id => client_key, :code => code, :client_secret => client_secret
+ end
+ it "returns an error if no client application matches" do
+ get :access_token, :client_id => "somekey", :code => code, :client_secret => client_secret
+ response.body.should eq({:error => "No client application found"}.to_json)
+ end
+ it "authorizes the access grant with the temporary code" do
+ AccessGrant.should_receive(:authorize).with(client_application, code)
+ get :access_token, :client_id => client_key, :code => code, :client_secret => client_secret
+ end
+ it "returns an error if no access grant matches" do
+ get :access_token, :client_id => client_key, :code => "somecode", :client_secret => client_secret
+ response.body.should eq({:error => "No grant found"}.to_json)
+ end
+ it "responds with an access token" do
+ get :access_token, :client_id => client_key, :code => code, :client_secret => client_secret
+ response.body.should eq({:access_token => access_grant.access_token}.to_json)
+ end
+ end
+end
View
12 spec/controllers/public_controller_spec.rb
@@ -0,0 +1,12 @@
+require 'spec_helper'
+
+describe PublicController do
+
+ describe "GET 'index'" do
+ it "returns http success" do
+ get 'index'
+ response.should be_success
+ end
+ end
+
+end
View
16 spec/factories.rb
@@ -0,0 +1,16 @@
+FactoryGirl.define do
+ factory :user do
+ sequence(:username) {|n| "testing#{n}" }
+ sequence(:email) {|n| "testing#{n}@factory.com" }
+ password "simplepass"
+ password_confirmation "simplepass"
+ end
+ factory :client_application do
+ name "My Client App"
+ description "A short description"
+ end
+ factory :access_grant do
+ user
+ client_application
+ end
+end
View
19 spec/models/access_grant_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe AccessGrant do
+ it { should belong_to :client_application }
+ it { should belong_to :user }
+ it "should generate tokens on creation" do
+ access_grant = AccessGrant.create! Factory.attributes_for(:access_grant)
+ access_grant.code.should_not be_nil
+ access_grant.access_token.should_not be_nil
+ end
+ describe "#authorize" do
+ it "should find an access grant with an application and temp code" do
+ access_grant = Factory(:access_grant)
+ client_application = access_grant.client_application
+ code = access_grant.code
+ AccessGrant.authorize(client_application, code).should == access_grant
+ end
+ end
+end
View
29 spec/models/client_application_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+describe ClientApplication do
+ it { should accept_values_for(:name, "My Application", "my_application", "my") }
+ it { should_not accept_values_for(:name, nil, "", "m") }
+ it { should accept_values_for(:description, "Description", "", "A longer description") }
+ it "generates a client key and secret on creation" do
+ client_application = Factory(:client_application)
+ client_application.key.should_not be_nil
+ client_application.secret.should_not be_nil
+ end
+ it "should not accept mass assigned key or secret" do
+ client_application = Factory(:client_application, :key => "somekey", :secret => "somesecret")
+ client_application.key.should_not == "somekey"
+ client_application.secret.should_not == "somesecret"
+ end
+ describe "#with_id" do
+ it "should search for ClientApplications by key" do
+ client_application = Factory(:client_application)
+ ClientApplication.with_id(client_application.key).should == client_application
+ end
+ end
+ describe "#authorize" do
+ it "should search for ClientApplications by key and secret" do
+ client_application = Factory(:client_application)
+ ClientApplication.authorize(client_application.key, client_application.secret).should == client_application
+ end
+ end
+end
View
33 spec/models/user_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe User do
+ it { should accept_values_for(:username, "janedoe", "jd", "jd23", "j_d") }
+ it { should_not accept_values_for(:username, "j", "jane doe", "", nil) }
+ it "should have unique usernames" do
+ user1 = Factory(:user)
+ user2 = Factory.build(:user, :username => user1.username)
+ user2.save
+ user2.should_not be_valid
+ end
+
+ describe "#find_for_database_authentication" do
+ let (:user) { Factory(:user) }
+ let (:conditions) { {:login => user.username} }
+ it "should find by username" do
+ User.find_for_database_authentication(conditions).should == user
+ end
+ let (:conditions) { {:login => user.email} }
+ it "should find by email address" do
+ User.find_for_database_authentication(conditions).should == user
+ end
+ end
+ describe "#find_for_token_authentication" do
+ let(:user) { Factory(:user) }
+ let(:client_application) { Factory(:client_application) }
+ let(:access_grant) { Factory(:access_grant, :client_application => client_application, :user => user) }
+ let(:conditions) { {"access_token" => access_grant.access_token} }
+ it "should find for access grant token" do
+ User.find_for_token_authentication(conditions).should == user
+ end
+ end
+end
View
5 spec/spec_helper.rb
@@ -19,15 +19,12 @@
config.mock_with :rspec
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
- config.fixture_path = "#{::Rails.root}/spec/fixtures"
+ # config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
#config.use_transactional_fixtures = true
- config.before :each do
- Mongoid.master.collections.select {|c| c.name !~ /system/ }.each(&:drop)
- end
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
View
3 spec/support/devise.rb
@@ -0,0 +1,3 @@
+Spec.configure do |config|
+ config.include Devise::TestHelpers, :type => :controller
+end
View
6 spec/support/mongoid.rb
@@ -0,0 +1,6 @@
+RSpec.configure do |config|
+ config.before :each do
+ Mongoid.master.collections.select {|c| c.name !~ /system/ }.each(&:drop)
+ end
+ config.include Mongoid::Matchers
+end

No commit comments for this range

Something went wrong with that request. Please try again.