thincloud-authentication 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +133 -0
  3. data/Rakefile +40 -0
  4. data/app/assets/javascripts/thincloud/authentication/application.js +15 -0
  5. data/app/assets/stylesheets/thincloud/authentication/application.css +13 -0
  6. data/app/controllers/thincloud/authentication/application_controller.rb +6 -0
  7. data/app/controllers/thincloud/authentication/registrations_controller.rb +90 -0
  8. data/app/controllers/thincloud/authentication/sessions_controller.rb +23 -0
  9. data/app/helpers/thincloud/authentication/registrations_helper.rb +9 -0
  10. data/app/mailers/thincloud/authentication/registrations_mailer.rb +12 -0
  11. data/app/models/thincloud/authentication/identity.rb +80 -0
  12. data/app/views/thincloud/authentication/layouts/application.html.erb +14 -0
  13. data/app/views/thincloud/authentication/registrations/_registration_form.html.erb +69 -0
  14. data/app/views/thincloud/authentication/registrations/new.html.erb +1 -0
  15. data/app/views/thincloud/authentication/registrations_mailer/verification_token.text.erb +5 -0
  16. data/app/views/thincloud/authentication/sessions/_login_form.html.erb +46 -0
  17. data/app/views/thincloud/authentication/sessions/new.html.erb +22 -0
  18. data/config/routes.rb +14 -0
  19. data/db/migrate/20120918233329_create_thincloud_authentication_identities.rb +19 -0
  20. data/lib/tasks/thincloud-authentication_tasks.rake +4 -0
  21. data/lib/thincloud-authentication.rb +9 -0
  22. data/lib/thincloud/authentication/authenticatable_controller.rb +64 -0
  23. data/lib/thincloud/authentication/configuration.rb +20 -0
  24. data/lib/thincloud/authentication/engine.rb +67 -0
  25. data/lib/thincloud/authentication/identifiable_user.rb +9 -0
  26. data/lib/thincloud/authentication/version.rb +5 -0
  27. metadata +287 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 New Leaders
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # Thincloud::Authentication
2
+
3
+ [![Build Status](https://secure.travis-ci.org/newleaders/thincloud-authentication.png)](http://travis-ci.org/newleaders/thincloud-authentication) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/newleaders/thincloud-authentication)
4
+
5
+ ## Description
6
+
7
+ A Rails Engine to provide authentication for Thincloud applications.
8
+
9
+ ## Requirements
10
+
11
+ This gem requires Rails 3.2+ and has been tested on the following versions:
12
+
13
+ * 3.2
14
+
15
+ This gem has been tested against the following Ruby versions:
16
+
17
+ * MRI 1.9.2
18
+ * MRI 1.9.3
19
+ * JRuby 1.6+ (with `JRUBY_OPTS=--1.9`)
20
+ * Rubinius 2.0.0dev (with `RBXOPT=-X19`)
21
+
22
+ This gem has been tested against the following database versions:
23
+
24
+ * MySQL 5.0, 5.5
25
+ * PostgreSQL 9.1, 9.2
26
+ * SQLite 3
27
+
28
+
29
+ ## Installation
30
+
31
+ Add this line to your application's Gemfile:
32
+
33
+ ``` ruby
34
+ gem "thincloud-authentication"
35
+ ```
36
+
37
+ * Run `bundle`
38
+ * Copy the migrations and prepare your databases:
39
+
40
+ ```
41
+ $ rake thincloud_authentication:install:migrations db:migrate db:test:prepare
42
+ ```
43
+
44
+ * Mount the engine in your `config/routes.rb` file:
45
+
46
+ ```ruby
47
+ mount Thincloud::Authentication::Engine => "/auth", as: "auth_engine"
48
+ ```
49
+
50
+ Using the example above, you may now login or signup at [http://lvh.me:3000/auth](http://lvh.me:3000/auth).
51
+
52
+ ### Prerequisites
53
+
54
+ The following must be true for `thincloud-authentication` to operate properly:
55
+
56
+ * A `root_url` must be defined in `config/routes.rb`
57
+ * A `User` model must exist
58
+
59
+
60
+ ## Configuration
61
+
62
+ The `Thincloud::Authentication` module accepts a `configure` block with options to customize the engine behavior.
63
+
64
+
65
+ ### Mailers
66
+
67
+ Set the `mailer_sender` option to customize the "From" address of the emails sent from the system:
68
+
69
+ ```ruby
70
+ Thincloud::Authentication.configure do |config|
71
+ config.mailer_sender = "app@example.com"
72
+ end
73
+ ```
74
+
75
+
76
+ ### Additional provider strategies
77
+
78
+ Add a key to the `providers` hash with the name of the strategy, followed by additional options for `scopes` and `fields` as needed. Additionally, you will need to provide environment variables (prefixed with the provider name), with the `consumer_key` and `consumer_secret` values from your OAuth provider.
79
+
80
+ To enable the [LinkedIn](https://github.com/skorks/omniauth-linkedin) provider:
81
+
82
+ * Provide values for `ENV["LINKEDIN_CONSUMER_KEY"]` and `ENV["LINKEDIN_CONSUMER_SECRET"]`
83
+ * Add the file `config/initializers/thincloud_authentication.rb` with the following contents:
84
+
85
+ ```ruby
86
+ Thincloud::Authentication.configure do |config|
87
+ config.providers[:linkedin] = {
88
+ scopes: "r_emailaddress r_basicprofile",
89
+ fields: ["id", "email-address", "first-name", "last-name", "headline",
90
+ "industry", "picture-url", "location", "public-profile-url"]
91
+ }
92
+ end
93
+ ```
94
+
95
+
96
+ ### Vanity Routes
97
+
98
+ If you want to customize the routes (remove the `/auth` prefix), you may add the following to your `config/routes.rb` file:
99
+
100
+ ```ruby
101
+ get "signup", to: "thincloud/authentication/registrations#new", as: "signup"
102
+ get "login", to: "thincloud/authentication/sessions#new", as: "login"
103
+ delete "logout", to: "thincloud/authentication/sessions#destroy", as: "logout"
104
+ ```
105
+
106
+ Using the example above, you will have the following routes locally:
107
+
108
+ * `signup_url` points to "/signup"
109
+ * `login_url` points to "/login"
110
+ * `logout_url` points to "/logout" - Make sure to use the `delete` method to logout.
111
+
112
+
113
+ ## TODO
114
+
115
+ * Add "forgot password" functionality
116
+ * Add multiple, configurable strategy options
117
+ * Add a configuration option to customize the mailers
118
+
119
+
120
+ ## Contributing
121
+
122
+ 1. [Fork it](https://github.com/newleaders/thincloud-authentication/fork_select)
123
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
124
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
125
+ 4. Push to the branch (`git push origin my-new-feature`)
126
+ 5. [Create a Pull Request](https://github.com/newleaders/thincloud-authentication/pull/new)
127
+
128
+
129
+ ## License
130
+
131
+ * Freely distributable and licensed under the [MIT license](http://newleaders.mit-license.org/2012/license.html).
132
+ * Copyright (c) 2012 New Leaders ([opensource@newleaders.com](opensource@newleaders.com))
133
+ * [https://newleaders.com](https://newleaders.com)
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'Thincloud::Authentication'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ require 'rake/testtask'
31
+
32
+ Rake::TestTask.new(:test) do |t|
33
+ t.libs << 'lib'
34
+ t.libs << 'test'
35
+ t.pattern = 'test/**/*_test.rb'
36
+ t.verbose = false
37
+ end
38
+
39
+
40
+ task :default => :test
@@ -0,0 +1,15 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require_tree .
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,6 @@
1
+ module Thincloud::Authentication
2
+ # Public: Primary controller settings and helpers for the engine.
3
+ class ApplicationController < ActionController::Base
4
+ layout "application"
5
+ end
6
+ end
@@ -0,0 +1,90 @@
1
+ require_dependency "thincloud/authentication/application_controller"
2
+
3
+ module Thincloud::Authentication
4
+ # Public: Handle OmniAuth callbacks.
5
+ class RegistrationsController < ApplicationController
6
+ before_filter :extract_identity, only: :create
7
+
8
+ def new
9
+ @identity = Identity.new
10
+ end
11
+
12
+ def create
13
+ # identity exists
14
+ if @identity.present?
15
+ login_as @identity.user
16
+ redirect_to main_app.root_url, notice: "You have been logged in."
17
+ # new identity for current_user
18
+ elsif current_user
19
+ add_omniauth_identity_to_current_user
20
+ redirect_to main_app.root_url, notice: "You have been logged in."
21
+ # failed identity login
22
+ elsif invalid_identity_credentials?
23
+ redirect_to auth_failure_url message: "invalid_credentials",
24
+ strategy: "identity"
25
+ # create a new identity
26
+ else
27
+ @identity = create_identity_from_request
28
+ render :new and return if @identity.errors.any?
29
+
30
+ if omniauth
31
+ login_as @identity.user
32
+ else
33
+ RegistrationsMailer.verification_token(@identity).deliver
34
+ flash[:alert] = "Check your email to verify your registration."
35
+ end
36
+ redirect_to main_app.root_url
37
+ end
38
+ end
39
+
40
+ def verify
41
+ identity = Identity.verify!(params[:token])
42
+ login_as identity.user
43
+ redirect_to main_app.root_url,
44
+ notice: "Thank you! Your registration has been verified."
45
+ end
46
+
47
+ private
48
+
49
+ # Private: Accessor for OmniAuth environment.
50
+ #
51
+ # Returns: An instance of `OmniAuth::InfoHash` or `nil`.
52
+ def omniauth
53
+ request.env["omniauth.auth"]
54
+ end
55
+
56
+ # Private: Set an instance varible if an `Identity` if present.
57
+ #
58
+ # Returns: An instance of `Identity` or `nil`.
59
+ def extract_identity
60
+ @identity = Identity.find_omniauth(omniauth) if omniauth
61
+ end
62
+
63
+ # Private: Determine if the request is from an invalid Identity login.
64
+ #
65
+ # Returns: Boolean.
66
+ def invalid_identity_credentials?
67
+ params[:provider] == "identity" && params.has_key?(:auth_key)
68
+ end
69
+
70
+ # Private: Add a new identity to the `current_user` from OmniAuth.
71
+ #
72
+ # Returns: Boolean.
73
+ def add_omniauth_identity_to_current_user
74
+ current_user.identities.build.apply_omniauth(omniauth).save
75
+ end
76
+
77
+ # Private: Create a new identity from submitted params or OmniAuth.
78
+ #
79
+ # Returns: An instance of `Identity`.
80
+ def create_identity_from_request
81
+ # params[:identity] exists when creating a local identity provider
82
+ Identity.new(params[:identity]).tap do |identity|
83
+ identity.user = User.create
84
+ # omniauth exists if coming from a 3rd party provider like LinkedIn
85
+ identity.apply_omniauth(omniauth) if omniauth
86
+ identity.save
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,23 @@
1
+ require_dependency "thincloud/authentication/application_controller"
2
+
3
+ module Thincloud::Authentication
4
+ # Public: Handle login/logout behavior.
5
+ class SessionsController < ApplicationController
6
+ before_filter :authenticate!, only: [:authenticated]
7
+
8
+ def new
9
+ redirect_to main_app.root_url if logged_in?
10
+ @identity = Identity.new
11
+ end
12
+
13
+ def destroy
14
+ logout
15
+ redirect_to main_app.root_url, notice: "You have been logged out."
16
+ end
17
+
18
+ def authenticated
19
+ # dummy method to test the :authenticate! before_filter
20
+ render text: "Authenticated!"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ module Thincloud::Authentication
2
+ module RegistrationsHelper
3
+
4
+ def form_error_class_for(form, field)
5
+ "error" if form.object.errors[field].present?
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ module Thincloud::Authentication
2
+ # Public: Email methods for Registration events
3
+ class RegistrationsMailer < ActionMailer::Base
4
+ default from: Thincloud::Authentication.configuration.mailer_sender
5
+
6
+ # New registration verification token
7
+ def verification_token(identity)
8
+ @identity = identity
9
+ mail to: @identity.email, subject: "Identity Verification"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,80 @@
1
+ module Thincloud::Authentication
2
+ # Public: This class represents a User identity (name, email, login provider)
3
+ class Identity < ::OmniAuth::Identity::Models::ActiveRecord
4
+ belongs_to :user
5
+
6
+ # Limit the ability to mass-assign sensitive fields.
7
+ attr_accessible :name, :email, :password, :password_confirmation
8
+
9
+ validates :name, presence: true
10
+ validates :email, presence: true, uniqueness: true, format: /@/
11
+
12
+ # Ensure that a `verification_token` exists for new records.
13
+ after_initialize do
14
+ self.verification_token = SecureRandom.urlsafe_base64 if new_record?
15
+ end
16
+
17
+ # Only validate password if the 'provider' is 'identity'.
18
+ before_validation do
19
+ self.password_digest = 0 unless provider == "identity"
20
+ end
21
+
22
+ # Public: Use a helpful attribute name when displaying errors.
23
+ def self.human_attribute_name(attr, options={})
24
+ attr == :password_digest ? "Password" : super
25
+ end
26
+
27
+ # Public: Find an `Identity` by OmniAuth parameters.
28
+ #
29
+ # omniauth - An instance of `OmniAuth::AuthHash`
30
+ #
31
+ # Returns: An instance of `Identity` or `nil`.
32
+ def self.find_omniauth(omniauth)
33
+ find_by_provider_and_uid omniauth["provider"], omniauth["uid"]
34
+ end
35
+
36
+ # Public: Mark the `Identity` as having been verified.
37
+ #
38
+ # token - A String containing the `verification_token` to look up.
39
+ #
40
+ # Returns: An instance of the found `Identity`.
41
+ # Raises: ActiveRecord::RecordNotFound if the `token` cannot be retrieved.
42
+ def self.verify!(token)
43
+ find_by_verification_token!(token).tap do |identity|
44
+ # ensure 'uid' exists, needed for 'identity' provider
45
+ identity.uid = identity.id if identity.uid.blank?
46
+ identity.verification_token = nil
47
+ identity.verified_at = Time.zone.now
48
+ identity.save
49
+ end
50
+ end
51
+
52
+ # Public: Shim to overcome odd behavior seen during testing with SQLite
53
+ def uid
54
+ read_attribute :uid
55
+ end
56
+
57
+ # Public: Indicate if the `Identity` has been verified.
58
+ #
59
+ # Returns: Boolean.
60
+ def verified?
61
+ verification_token.blank? && verified_at.present?
62
+ end
63
+
64
+ # Public: Apply attributes returned from OmniAuth.
65
+ #
66
+ # omniauth - An instance of `OmniAuth::AuthHash`.
67
+ def apply_omniauth(omniauth)
68
+ info = omniauth["info"]
69
+
70
+ user_name = %Q(#{info["first_name"]} #{info["last_name"]})
71
+ user_name.gsub!(/\s+/, " ").strip!
72
+
73
+ self.provider = omniauth["provider"]
74
+ self.uid = omniauth["uid"]
75
+ self.name = user_name if self.name.blank?
76
+ self.email = info["email"] if info["email"] && self.email.blank?
77
+ self
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Thincloud::Authentication</title>
5
+ <%= stylesheet_link_tag "thincloud/authentication/application", :media => "all" %>
6
+ <%= javascript_include_tag "thincloud/authentication/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,69 @@
1
+ <%= form_for @identity, url: registrations_path, html: { class: "form-horizontal" } do |f| %>
2
+ <fieldset>
3
+ <legend>Register</legend>
4
+
5
+ <% if @identity.errors.any? %>
6
+ <div class="alert alert-block alert-error">
7
+ <h4><i class="icon-warning-sign"></i> Error</h4>
8
+ Please check the following <%= pluralize @identity.errors.size, "item" %>:
9
+ </div>
10
+ <% end %>
11
+
12
+ <div class="control-group <%= form_error_class_for(f, :name) %>">
13
+ <%= f.label :name, "Name", class: "control-label" %>
14
+ <div class="controls">
15
+ <div class="input-prepend">
16
+ <span class="add-on"><i class="icon-user"></i></span>
17
+ <%= f.text_field :name %>
18
+ </div>
19
+ <%= content_tag :span, "#{:name.to_s.humanize} #{f.object.errors[:name].to_sentence}", class: "help-inline" %>
20
+ </div>
21
+ </div>
22
+
23
+
24
+ <div class="control-group <%= form_error_class_for(f, :email) %>">
25
+ <%= f.label :email, "Email", class: "control-label" %>
26
+ <div class="controls">
27
+ <div class="input-prepend">
28
+ <span class="add-on"><i class="icon-envelope"></i></span>
29
+ <%= f.email_field :email %>
30
+ </div>
31
+ <%= content_tag :span, "#{:email.to_s.humanize} #{f.object.errors[:email].to_sentence}", class: "help-inline" %>
32
+ </div>
33
+ </div>
34
+
35
+ <div class="control-group <%= form_error_class_for(f, :password_digest) %>">
36
+ <%= f.label :password, "Password", class: "control-label" %>
37
+ <div class="controls">
38
+ <div class="input-prepend">
39
+ <span class="add-on"><i class="icon-lock"></i></span>
40
+ <%= f.password_field :password %>
41
+ </div>
42
+ <%= content_tag :span, "#{:password.to_s.humanize} #{f.object.errors[:password_digest].to_sentence}", class: "help-inline" %>
43
+ </div>
44
+ </div>
45
+
46
+ <div class="control-group <%= form_error_class_for(f, :password) %>">
47
+ <%= f.label :password_confirmation, "Confirmation", class: "control-label" %>
48
+ <div class="controls">
49
+ <div class="input-prepend">
50
+ <span class="add-on"><i class="icon-lock"></i></span>
51
+ <%= f.password_field :password_confirmation %>
52
+ </div>
53
+ <%= content_tag :span, "#{:password.to_s.humanize} #{f.object.errors[:password].to_sentence}", class: "help-inline" %>
54
+ </div>
55
+ </div>
56
+
57
+ <div class="control-group">
58
+ <div class="controls">
59
+ <%= button_tag type: "submit", class: "btn btn-large btn-primary" do %>
60
+ <i class="icon-ok icon-white"></i> Register
61
+ <% end %>
62
+
63
+ <%= link_to root_url, class: "btn btn-large" do %>
64
+ <i class="icon-remove"></i> Cancel
65
+ <% end %>
66
+ </div>
67
+ </div>
68
+ </fieldset>
69
+ <% end %>
@@ -0,0 +1 @@
1
+ <%= render "registration_form" %>
@@ -0,0 +1,5 @@
1
+ Welcome <%= @identity.email %>!
2
+
3
+ You can verify your account through the link below:
4
+
5
+ <%= verify_token_url(token: @identity.verification_token) %>
@@ -0,0 +1,46 @@
1
+ <%= form_tag auth_callback_url(provider: "identity"), class: "form-horizontal" do %>
2
+ <fieldset>
3
+ <legend>Login</legend>
4
+
5
+ <% if params[:message].present? %>
6
+ <div class="alert alert-block alert-error">
7
+ <h4><i class="icon-warning-sign"></i> Error</h4>
8
+ <%= params[:message].humanize %>
9
+ </div>
10
+ <% end %>
11
+
12
+ <div class="control-group">
13
+ <%= label_tag :auth_key, "Email", class: "control-label" %>
14
+ <div class="controls">
15
+ <div class="input-prepend">
16
+ <span class="add-on"><i class="icon-envelope"></i></span>
17
+ <%= email_field_tag :auth_key, params[:auth_key] %>
18
+ </div>
19
+ </div>
20
+ </div>
21
+
22
+ <div class="control-group">
23
+ <%= label_tag :password, "Password", class: "control-label" %>
24
+ <div class="controls">
25
+ <div class="input-prepend">
26
+ <span class="add-on"><i class="icon-lock"></i></span>
27
+ <%= password_field_tag :password %>
28
+ </div>
29
+ </div>
30
+ </div>
31
+
32
+ <div class="control-group">
33
+ <div class="controls">
34
+ <%= button_tag type: "submit", class: "btn btn-large btn-primary" do %>
35
+ <i class="icon-ok icon-white"></i> Login
36
+ <% end %>
37
+
38
+ or
39
+
40
+ <%= link_to signup_url, class: "btn btn-large" do %>
41
+ <i class="icon-user"></i> Signup
42
+ <% end %>
43
+ </div>
44
+ </div>
45
+ </fieldset>
46
+ <% end %>
@@ -0,0 +1,22 @@
1
+ <div class="tabbable">
2
+ <ul class="nav nav-tabs">
3
+ <li class="active">
4
+ <a href="#login" data-toggle="tab"><i class="icon-lock"></i> Login</a>
5
+ </li>
6
+ <li>
7
+ <a href="#register" data-toggle="tab"><i class="icon-user"></i> Register</a>
8
+ </li>
9
+ </ul>
10
+
11
+ <div class="tab-content">
12
+ <!-- Login -->
13
+ <div class="tab-pane active" id="login">
14
+ <%= render "login_form" %>
15
+ </div>
16
+
17
+ <!-- Register -->
18
+ <div class="tab-pane" id="register">
19
+ <%= render "thincloud/authentication/registrations/registration_form" %>
20
+ </div>
21
+ </div>
22
+ </div>
data/config/routes.rb ADDED
@@ -0,0 +1,14 @@
1
+ Thincloud::Authentication::Engine.routes.draw do
2
+ match ":provider/callback" => "registrations#create", as: "auth_callback"
3
+ get "failure", to: "sessions#new", as: "auth_failure"
4
+
5
+ get "login", to: "sessions#new", as: "login"
6
+ delete "logout", to: "sessions#destroy", as: "logout"
7
+ get "authenticated", to: "sessions#authenticated"
8
+
9
+ resources :registrations, only: [:new, :create]
10
+ get "signup", to: "registrations#new", as: "signup"
11
+ get "verify/:token", to: "registrations#verify", as: "verify_token"
12
+
13
+ root to: "sessions#new"
14
+ end
@@ -0,0 +1,19 @@
1
+ class CreateThincloudAuthenticationIdentities < ActiveRecord::Migration
2
+ def change
3
+ create_table :thincloud_authentication_identities do |t|
4
+ t.integer :user_id, null: false
5
+ t.string :provider, null: false, default: "identity"
6
+ t.string :uid
7
+ t.string :name, null: false
8
+ t.string :email, null: false
9
+ t.string :password_digest, null: false
10
+ t.string :verification_token
11
+ t.datetime :verified_at
12
+
13
+ t.timestamps
14
+ end
15
+ add_index :thincloud_authentication_identities, :user_id
16
+ add_index :thincloud_authentication_identities, [:provider, :uid], unique: true
17
+ add_index :thincloud_authentication_identities, :email
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :thincloud-authentication do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,9 @@
1
+ require "thincloud/authentication/configuration"
2
+ require "thincloud/authentication/engine"
3
+ require "thincloud/authentication/authenticatable_controller"
4
+ require "thincloud/authentication/identifiable_user"
5
+
6
+ module Thincloud
7
+ module Authentication
8
+ end
9
+ end
@@ -0,0 +1,64 @@
1
+ module Thincloud
2
+ module Authentication
3
+
4
+ module AuthenticatableController
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ helper_method :current_user
9
+ helper_method :logged_in?
10
+ end
11
+
12
+ protected
13
+
14
+ # Protected: The user that is currently logged in.
15
+ #
16
+ # This method is also available as a view helper.
17
+ #
18
+ # Returns: An instance of `User` or `nil`.
19
+ def current_user
20
+ return nil if session[:uid].blank?
21
+ @current_user ||= User.find(session[:uid])
22
+ end
23
+
24
+ # Protected: Determine if the current request has a logged in user.
25
+ #
26
+ # This method is also available as a view helper.
27
+ #
28
+ # Returns: Boolean.
29
+ def logged_in?
30
+ current_user.present?
31
+ end
32
+
33
+ # Protected: Require an authenticated user to perform an action.
34
+ #
35
+ # Use in a `before_filter`.
36
+ #
37
+ # Returns: Redirect if not logged in, otherwise `nil`.
38
+ def authenticate!
39
+ unless logged_in?
40
+ redirect_to login_url, alert: "You must be logged in to continue."
41
+ end
42
+ end
43
+
44
+ # Protected: Set the `current_user` to the provided `User` instance.
45
+ #
46
+ # user - An instance of `User` that has been authenticated.
47
+ #
48
+ # Returns: The `id` of the provided user.
49
+ def login_as(user)
50
+ reset_session # avoid session fixation
51
+ session[:uid] = user.id
52
+ end
53
+
54
+ # Protected: Clear the session of an authenticated user.
55
+ #
56
+ # Returns: A new empty session instance.
57
+ def logout
58
+ reset_session
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,20 @@
1
+ module Thincloud::Authentication
2
+ class << self
3
+ attr_accessor :configuration
4
+ end
5
+
6
+ def self.configure
7
+ self.configuration ||= Configuration.new
8
+ yield configuration
9
+ end
10
+
11
+ # Public: Configuration options for the Thincloud::Authentication module
12
+ class Configuration
13
+ attr_accessor :providers, :mailer_sender
14
+
15
+ def initialize
16
+ @providers = {}
17
+ @mailer_sender = "app@example.com"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,67 @@
1
+ module Thincloud
2
+ module Authentication
3
+ # Public: Initialize the Rails engine
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace Thincloud::Authentication
6
+
7
+ initializer "thincloud.authentication.omniauth.middleware" do |app|
8
+ require "omniauth"
9
+ require "omniauth-identity"
10
+
11
+ config = Thincloud::Authentication.configuration || Configuration.new
12
+ strategies = config.providers.keys
13
+ strategies.each { |strategy| require "omniauth-#{strategy}" }
14
+
15
+ app.middleware.use ::OmniAuth::Builder do
16
+
17
+ # always provide the Identity strategy
18
+ provider :identity, fields: [:email], model: Identity,
19
+ on_failed_registration: RegistrationsController.action(:new)
20
+
21
+ strategies.each do |strategy|
22
+ provider strategy, ENV["#{strategy.to_s.upcase}_CONSUMER_KEY"],
23
+ ENV["#{strategy.to_s.upcase}_CONSUMER_SECRET"],
24
+ fields: config.providers[strategy][:fields],
25
+ scope: config.providers[strategy][:scopes]
26
+ end
27
+ end
28
+ end
29
+
30
+ initializer "thincloud.authentication.omniauth.logger" do
31
+ ::OmniAuth.config.logger = ::Rails.logger
32
+ end
33
+
34
+ initializer "thincloud.authentication.omniauth.failure_endpoint" do
35
+ ::OmniAuth.config.on_failure = -> env do
36
+ ::OmniAuth::FailureEndpoint.new(env).redirect_to_failure
37
+ end
38
+ end
39
+
40
+ initializer "thincloud.authentication.omniauth.identity_redirects" do
41
+ # override default omniauth-identity forms
42
+ class ::OmniAuth::Strategies::Identity
43
+ def registration_form
44
+ redirect "/signup"
45
+ end
46
+
47
+ def request_phase
48
+ redirect "/login"
49
+ end
50
+ end
51
+ end
52
+
53
+ initializer "thincloud.authentication.user" do
54
+ ::User.send :include, Thincloud::Authentication::IdentifiableUser
55
+ end
56
+
57
+ initializer "thincloud.authentication.action_controller" do
58
+ ActionController::Base.send :include,
59
+ Thincloud::Authentication::AuthenticatableController
60
+ end
61
+
62
+ config.generators do |g|
63
+ g.test_framework :mini_test, spec: true, fixture: false
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,9 @@
1
+ module Thincloud::Authentication
2
+ module IdentifiableUser
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ has_many :identities, class_name: "Thincloud::Authentication::Identity"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ module Thincloud
2
+ module Authentication
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,287 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: thincloud-authentication
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Phil Cohen
9
+ - Robert Bousquet
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-09-23 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rails
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: 3.2.8
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ version: 3.2.8
31
+ - !ruby/object:Gem::Dependency
32
+ name: omniauth
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ~>
37
+ - !ruby/object:Gem::Version
38
+ version: 1.1.1
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: 1.1.1
47
+ - !ruby/object:Gem::Dependency
48
+ name: omniauth-identity
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.1.0
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ version: 1.1.0
63
+ - !ruby/object:Gem::Dependency
64
+ name: omniauth-linkedin
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ~>
69
+ - !ruby/object:Gem::Version
70
+ version: 0.0.8
71
+ type: :runtime
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ~>
77
+ - !ruby/object:Gem::Version
78
+ version: 0.0.8
79
+ - !ruby/object:Gem::Dependency
80
+ name: cane
81
+ requirement: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ~>
85
+ - !ruby/object:Gem::Version
86
+ version: 2.3.0
87
+ type: :development
88
+ prerelease: false
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ~>
93
+ - !ruby/object:Gem::Version
94
+ version: 2.3.0
95
+ - !ruby/object:Gem::Dependency
96
+ name: guard
97
+ requirement: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ~>
101
+ - !ruby/object:Gem::Version
102
+ version: 1.3.3
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: 1.3.3
111
+ - !ruby/object:Gem::Dependency
112
+ name: minitest
113
+ requirement: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ~>
117
+ - !ruby/object:Gem::Version
118
+ version: 3.4.0
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ~>
125
+ - !ruby/object:Gem::Version
126
+ version: 3.4.0
127
+ - !ruby/object:Gem::Dependency
128
+ name: guard-minitest
129
+ requirement: !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ~>
133
+ - !ruby/object:Gem::Version
134
+ version: 0.5.0
135
+ type: :development
136
+ prerelease: false
137
+ version_requirements: !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ~>
141
+ - !ruby/object:Gem::Version
142
+ version: 0.5.0
143
+ - !ruby/object:Gem::Dependency
144
+ name: minitest-rails
145
+ requirement: !ruby/object:Gem::Requirement
146
+ none: false
147
+ requirements:
148
+ - - ~>
149
+ - !ruby/object:Gem::Version
150
+ version: 0.1.3
151
+ type: :development
152
+ prerelease: false
153
+ version_requirements: !ruby/object:Gem::Requirement
154
+ none: false
155
+ requirements:
156
+ - - ~>
157
+ - !ruby/object:Gem::Version
158
+ version: 0.1.3
159
+ - !ruby/object:Gem::Dependency
160
+ name: minitest-rails-shoulda
161
+ requirement: !ruby/object:Gem::Requirement
162
+ none: false
163
+ requirements:
164
+ - - ~>
165
+ - !ruby/object:Gem::Version
166
+ version: 0.1.0
167
+ type: :development
168
+ prerelease: false
169
+ version_requirements: !ruby/object:Gem::Requirement
170
+ none: false
171
+ requirements:
172
+ - - ~>
173
+ - !ruby/object:Gem::Version
174
+ version: 0.1.0
175
+ - !ruby/object:Gem::Dependency
176
+ name: rb-fsevent
177
+ requirement: !ruby/object:Gem::Requirement
178
+ none: false
179
+ requirements:
180
+ - - ~>
181
+ - !ruby/object:Gem::Version
182
+ version: 0.9.1
183
+ type: :development
184
+ prerelease: false
185
+ version_requirements: !ruby/object:Gem::Requirement
186
+ none: false
187
+ requirements:
188
+ - - ~>
189
+ - !ruby/object:Gem::Version
190
+ version: 0.9.1
191
+ - !ruby/object:Gem::Dependency
192
+ name: simplecov
193
+ requirement: !ruby/object:Gem::Requirement
194
+ none: false
195
+ requirements:
196
+ - - ~>
197
+ - !ruby/object:Gem::Version
198
+ version: 0.6.4
199
+ type: :development
200
+ prerelease: false
201
+ version_requirements: !ruby/object:Gem::Requirement
202
+ none: false
203
+ requirements:
204
+ - - ~>
205
+ - !ruby/object:Gem::Version
206
+ version: 0.6.4
207
+ - !ruby/object:Gem::Dependency
208
+ name: mocha
209
+ requirement: !ruby/object:Gem::Requirement
210
+ none: false
211
+ requirements:
212
+ - - ~>
213
+ - !ruby/object:Gem::Version
214
+ version: 0.12.4
215
+ type: :development
216
+ prerelease: false
217
+ version_requirements: !ruby/object:Gem::Requirement
218
+ none: false
219
+ requirements:
220
+ - - ~>
221
+ - !ruby/object:Gem::Version
222
+ version: 0.12.4
223
+ description: Rails Engine to provide authentication for Thincloud applications
224
+ email:
225
+ - pcohen@newleaders.com
226
+ - rbousquet@newleaders.com
227
+ executables: []
228
+ extensions: []
229
+ extra_rdoc_files: []
230
+ files:
231
+ - app/assets/javascripts/thincloud/authentication/application.js
232
+ - app/assets/stylesheets/thincloud/authentication/application.css
233
+ - app/controllers/thincloud/authentication/application_controller.rb
234
+ - app/controllers/thincloud/authentication/registrations_controller.rb
235
+ - app/controllers/thincloud/authentication/sessions_controller.rb
236
+ - app/helpers/thincloud/authentication/registrations_helper.rb
237
+ - app/mailers/thincloud/authentication/registrations_mailer.rb
238
+ - app/models/thincloud/authentication/identity.rb
239
+ - app/views/thincloud/authentication/layouts/application.html.erb
240
+ - app/views/thincloud/authentication/registrations/_registration_form.html.erb
241
+ - app/views/thincloud/authentication/registrations/new.html.erb
242
+ - app/views/thincloud/authentication/registrations_mailer/verification_token.text.erb
243
+ - app/views/thincloud/authentication/sessions/_login_form.html.erb
244
+ - app/views/thincloud/authentication/sessions/new.html.erb
245
+ - config/routes.rb
246
+ - db/migrate/20120918233329_create_thincloud_authentication_identities.rb
247
+ - lib/tasks/thincloud-authentication_tasks.rake
248
+ - lib/thincloud/authentication/authenticatable_controller.rb
249
+ - lib/thincloud/authentication/configuration.rb
250
+ - lib/thincloud/authentication/engine.rb
251
+ - lib/thincloud/authentication/identifiable_user.rb
252
+ - lib/thincloud/authentication/version.rb
253
+ - lib/thincloud-authentication.rb
254
+ - MIT-LICENSE
255
+ - Rakefile
256
+ - README.md
257
+ homepage: https://github.com/newleaders/thincloud-authentication
258
+ licenses: []
259
+ post_install_message:
260
+ rdoc_options: []
261
+ require_paths:
262
+ - lib
263
+ required_ruby_version: !ruby/object:Gem::Requirement
264
+ none: false
265
+ requirements:
266
+ - - ! '>='
267
+ - !ruby/object:Gem::Version
268
+ version: '0'
269
+ segments:
270
+ - 0
271
+ hash: 3364386958863775653
272
+ required_rubygems_version: !ruby/object:Gem::Requirement
273
+ none: false
274
+ requirements:
275
+ - - ! '>='
276
+ - !ruby/object:Gem::Version
277
+ version: '0'
278
+ segments:
279
+ - 0
280
+ hash: 3364386958863775653
281
+ requirements: []
282
+ rubyforge_project:
283
+ rubygems_version: 1.8.24
284
+ signing_key:
285
+ specification_version: 3
286
+ summary: Rails Engine to provide authentication for Thincloud applications
287
+ test_files: []