passwordless 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 75c3878f6173f614504aec1073969f86c5c62b52f987c2c3402ea75771d705b4
4
- data.tar.gz: d5f7d2cbd330c33ac40213860653087a2d8977bcad1d936431fd5c41a03e60e7
3
+ metadata.gz: a3dbc0b122174ab4d4f115f47ef829bfe0ccfdf49cb559725e8833d5abaef517
4
+ data.tar.gz: ba545dc28ee08852e3c2caf2c9d920343aac14d2039bd3dc4f48e399d99c8bf4
5
5
  SHA512:
6
- metadata.gz: 0a1c0c33379fc8da5b45fb26e1bf89d27848be3c0d627bdf84257966cf5256e85c0e5e2129f5da2f39602a8346649d082dc9de1387886110580d1693619df922
7
- data.tar.gz: 59528a2ec29c12b02de00616fd9b184b998d20d04124b5dbe08143fe7b9ca77f632395b60ae18052500b5b0ea84d9e4b7307df50200fed2a621d52c893106824
6
+ metadata.gz: 79e1c14bcc3d71e438f0f7b146642460d3116a3d681a2deb17bc6e860d819b990e68565de94a72caa640fa5958fc9587769b6ee0c6af7b16a2e3bac2475d6605
7
+ data.tar.gz: 1695fc3178204892a35ee1a35869836b09d26005f4b4e4c99d2e9d02c813a87a1fefad6f00664302f191ecb4f3e3fe9cc57888aeadf2b8a7c6355fb1f1581873
data/Rakefile CHANGED
@@ -1,22 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'bundler/setup'
3
5
  rescue LoadError
4
6
  puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
7
  end
6
8
 
7
- require 'rdoc/task'
8
-
9
- RDoc::Task.new(:rdoc) do |rdoc|
10
- rdoc.rdoc_dir = 'rdoc'
11
- rdoc.title = 'Passwordless'
12
- rdoc.options << '--line-numbers'
13
- rdoc.rdoc_files.include('README.md')
14
- rdoc.rdoc_files.include('lib/**/*.rb')
15
- end
9
+ require 'yard'
10
+ YARD::Rake::YardocTask.new
11
+ task docs: :yard
16
12
 
17
- APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
13
+ APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__)
18
14
  load 'rails/tasks/engine.rake'
19
-
20
15
  load 'rails/tasks/statistics.rake'
21
16
 
22
17
  require 'bundler/gem_tasks'
@@ -1,5 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Passwordless
4
+ # Base for Passwordless controllers
2
5
  class ApplicationController < ::ApplicationController
6
+ # Always returns true. Use to check if <Some>Controller inherits
7
+ # from ApplicationController.
8
+ # @return [boolean]
3
9
  def passwordless_controller?
4
10
  true
5
11
  end
@@ -1,22 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bcrypt'
2
4
 
3
5
  module Passwordless
6
+ # Controller for managing Passwordless sessions
4
7
  class SessionsController < ApplicationController
5
8
  include ControllerHelpers
6
9
 
7
10
  helper_method :authenticatable_resource
8
11
 
12
+ # get '/sign_in'
13
+ # Assigns an email_field and new Session to be used by new view.
14
+ # renders sessions/new.html.erb.
9
15
  def new
10
- @email_field = authenticatable_class.passwordless_email_field
11
-
16
+ @email_field = email_field
12
17
  @session = Session.new
13
18
  end
14
19
 
20
+ # post '/sign_in'
21
+ # Creates a new Session record then sends the magic link
22
+ # renders sessions/create.html.erb.
23
+ # @see Mailer#magic_link Mailer#magic_link
15
24
  def create
16
- email_field = authenticatable_class.passwordless_email_field
17
- email = params.require(:passwordless).fetch(email_field).downcase
18
- authenticatable =
19
- authenticatable_class.where("lower(#{email_field}) = ?", email).first
25
+ authenticatable = find_authenticatable
20
26
 
21
27
  session = Session.new.tap do |us|
22
28
  us.remote_addr = request.remote_addr
@@ -31,27 +37,32 @@ module Passwordless
31
37
  render
32
38
  end
33
39
 
40
+ # get '/sign_in/:token'
41
+ # Looks up session record by provided token. Signs in user if a match
42
+ # is found. Redirects to either the user's original destination
43
+ # or _root_path_
44
+ # @see ControllerHelpers#sign_in
45
+ # @see ControllerHelpers#save_passwordless_redirect_location!
34
46
  def show
35
47
  # Make it "slow" on purpose to make brute-force attacks more of a hassle
36
48
  BCrypt::Password.create(params[:token])
37
49
 
38
- session = Session.valid.find_by!(
39
- authenticatable_type: authenticatable_classname,
40
- token: params[:token]
41
- )
42
-
50
+ session = find_session
43
51
  sign_in session.authenticatable
44
52
 
45
- enabled = Passwordless.redirect_back_after_sign_in
46
- destination = dest = reset_passwordless_redirect_location!(User)
53
+ redirect_enabled = Passwordless.redirect_back_after_sign_in
54
+ destination = reset_passwordless_redirect_location!(User)
47
55
 
48
- if enabled && destination
49
- redirect_to dest
56
+ if redirect_enabled && destination
57
+ redirect_to destination
50
58
  else
51
59
  redirect_to main_app.root_path
52
60
  end
53
61
  end
54
62
 
63
+ # match '/sign_out', via: %i[get delete].
64
+ # Signs user out. Redirects to root_path
65
+ # @see ControllerHelpers#sign_out
55
66
  def destroy
56
67
  sign_out authenticatable_class
57
68
  redirect_to main_app.root_path
@@ -74,5 +85,22 @@ module Passwordless
74
85
  def authenticatable_resource
75
86
  authenticatable.pluralize
76
87
  end
88
+
89
+ def email_field
90
+ authenticatable_class.passwordless_email_field
91
+ end
92
+
93
+ def find_authenticatable
94
+ authenticatable_class.where(
95
+ "lower(#{email_field}) = ?", params[:passwordless][email_field]
96
+ ).first
97
+ end
98
+
99
+ def find_session
100
+ Session.valid.find_by!(
101
+ authenticatable_type: authenticatable_classname,
102
+ token: params[:token]
103
+ )
104
+ end
77
105
  end
78
106
  end
@@ -1,7 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Passwordless
4
+ # The mailer responsible for sending Passwordless' mails.
2
5
  class Mailer < ActionMailer::Base
3
6
  default from: Passwordless.default_from_address
4
7
 
8
+ # Sends a magic link (secret token) email.
9
+ # @param session [Session] A Passwordless Session
5
10
  def magic_link(session)
6
11
  @session = session
7
12
 
@@ -11,7 +16,10 @@ module Passwordless
11
16
  send(authenticatable_resource_name).token_sign_in_url(session.token)
12
17
 
13
18
  email_field = @session.authenticatable.class.passwordless_email_field
14
- mail to: @session.authenticatable.send(email_field), subject: "Your magic link ✨"
19
+ mail(
20
+ to: @session.authenticatable.send(email_field),
21
+ subject: 'Your magic link ✨'
22
+ )
15
23
  end
16
24
  end
17
25
  end
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Passwordless
4
+ # Base record for Passordless' models
2
5
  class ApplicationRecord < ActiveRecord::Base
3
6
  self.abstract_class = true
4
7
  end
@@ -1,6 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Passwordless
4
+ # The session responsible for holding the connection between the record
5
+ # trying to log in and the unique tokens.
2
6
  class Session < ApplicationRecord
3
- belongs_to :authenticatable, polymorphic: true
7
+ belongs_to :authenticatable,
8
+ polymorphic: true, inverse_of: :passwordless_sessions
4
9
 
5
10
  validates \
6
11
  :timeout_at,
data/config/routes.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Passwordless::Engine.routes.draw do
2
4
  get '/sign_in', to: 'sessions#new', as: :sign_in
3
5
  post '/sign_in', to: 'sessions#create'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class CreatePasswordlessSessions < ActiveRecord::Migration[5.1]
2
4
  def change
3
5
  create_table :passwordless_sessions do |t|
data/lib/passwordless.rb CHANGED
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'passwordless/engine'
2
4
  require 'passwordless/url_safe_base_64_generator'
3
5
 
6
+ # The main Passwordless module
4
7
  module Passwordless
5
8
  mattr_accessor(:default_from_address) { 'CHANGE_ME@example.com' }
6
- mattr_accessor(:token_generator) do
7
- UrlSafeBase64Generator.new
8
- end
9
+ mattr_accessor(:token_generator) { UrlSafeBase64Generator.new }
9
10
  mattr_accessor(:redirect_back_after_sign_in) { true }
10
11
  end
@@ -1,5 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Passwordless
4
+ # Helpers to work with Passwordless sessions from controllers
2
5
  module ControllerHelpers
6
+ # Authenticate a record using cookies. Looks for a cookie corresponding to
7
+ # the _authenticatable_class_. If found try to find it in the database.
8
+ # @param authenticatable_class [ActiveRecord::Base] any Model connected to
9
+ # passwordless. (e.g - _User_ or _Admin_).
10
+ # @return [ActiveRecord::Base|nil] an instance of Model found by id stored
11
+ # in cookies.encrypted or nil if nothing is found.
12
+ # @see ModelHelpers#passwordless_with
3
13
  def authenticate_by_cookie(authenticatable_class)
4
14
  key = cookie_name(authenticatable_class)
5
15
  authenticatable_id = cookies.encrypted[key]
@@ -8,22 +18,39 @@ module Passwordless
8
18
  authenticatable_class.find_by(id: authenticatable_id)
9
19
  end
10
20
 
21
+ # Signs in user by assigning their id to a permanent cookie.
22
+ # @param authenticatable [ActiveRecord::Base] Instance of Model to sign in
23
+ # (e.g - @user when @user = User.find(id: some_id)).
24
+ # @return [ActiveRecord::Base] the record that is passed in.
11
25
  def sign_in(authenticatable)
12
26
  key = cookie_name(authenticatable.class)
13
27
  cookies.encrypted.permanent[key] = { value: authenticatable.id }
14
28
  authenticatable
15
29
  end
16
30
 
31
+ # Signs out user by deleting their encrypted cookie.
32
+ # @param (see #authenticate_by_cookie)
33
+ # @return [boolean] Always true
17
34
  def sign_out(authenticatable_class)
18
35
  key = cookie_name(authenticatable_class)
19
36
  cookies.encrypted.permanent[key] = { value: nil }
20
37
  cookies.delete(key)
38
+ true
21
39
  end
22
40
 
41
+ # Saves request.original_url as the redirect location for a
42
+ # passwordless Model.
43
+ # @param (see #authenticate_by_cookie)
44
+ # @return [String] the redirect url that was just saved.
23
45
  def save_passwordless_redirect_location!(authenticatable_class)
24
46
  session[session_key(authenticatable_class)] = request.original_url
25
47
  end
26
48
 
49
+ # Resets the redirect_location to root_path by deleting the redirect_url
50
+ # from session.
51
+ # @param (see #authenticate_by_cookie)
52
+ # @return [String, nil] the redirect url that was just deleted,
53
+ # or nil if no url found for given Model.
27
54
  def reset_passwordless_redirect_location!(authenticatable_class)
28
55
  session.delete session_key(authenticatable_class)
29
56
  end
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Passwordless
4
+ # Engine that runs the passwordless gem.
2
5
  class Engine < ::Rails::Engine
3
6
  isolate_namespace Passwordless
4
7
 
@@ -1,6 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Passwordless
4
+ # Some helpers for models that can sign in passswordlessly.
2
5
  module ModelHelpers
6
+ # Creates relationship - has_many :passwordless_sessions
7
+ # Defines a method `Class.passwordless_email_field` returning its email
8
+ # field name (e.g. `:email`)
9
+ # @param field [string] email submitted by user.
3
10
  def passwordless_with(field)
11
+ has_many :passwordless_sessions, class_name: 'Passwordless::Session'
4
12
  define_singleton_method(:passwordless_email_field) { field }
5
13
  end
6
14
  end
@@ -1,5 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Passwordless
4
+ # Helpers for generating passwordless routes.
2
5
  module RouterHelpers
6
+ # Generates passwordless routes for a given Model
7
+ # Example usage:
8
+ # passwordless_for :users
9
+ # # or with options ...
10
+ # passwordless_for :users, at: 'session_stuff', as: :user_session_things
11
+ # @param resource [Symbol] the pluralized symbol of a Model (e.g - :users).
12
+ # @param at [String] Optional - provide custom path for the passwordless
13
+ # engine to get mounted at (using the above example your URLs end
14
+ # up like: /session_stuff/sign_in). (Default: resource.to_s)
15
+ # @param as [Symbol] Optional - provide custom scope for url
16
+ # helpers (using the above example in a view:
17
+ # <%= link_to 'Sign in', user_session_things.sign_in_path %>).
18
+ # (Default: resource.to_s)
3
19
  def passwordless_for(resource, at: nil, as: nil)
4
20
  mount(
5
21
  Passwordless::Engine,
@@ -1,5 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Passwordless
4
+ # Generates random numbers for Session records
2
5
  class UrlSafeBase64Generator
6
+ # Passwordless' default random string strategy. Generates a url safe
7
+ # base64 random string.
8
+ # @param _session [Session] Optional - Passwordless passes the sesion Record
9
+ # to generators so you can (optionally) use it for generating your tokens.
10
+ # @return [String] 32 byte base64 string
3
11
  def call(_session)
4
12
  SecureRandom.urlsafe_base64(32)
5
13
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Passwordless
2
- VERSION = '0.4.2'
4
+ VERSION = '0.4.3' # :nodoc:
3
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: passwordless
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikkel Malmberg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-24 00:00:00.000000000 Z
11
+ date: 2017-12-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -52,7 +52,35 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
- description: Description of Passwordless.
55
+ - !ruby/object:Gem::Dependency
56
+ name: yard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description:
56
84
  email:
57
85
  - mikkel@brnbw.com
58
86
  executables: []
@@ -79,7 +107,6 @@ files:
79
107
  - lib/passwordless/router_helpers.rb
80
108
  - lib/passwordless/url_safe_base_64_generator.rb
81
109
  - lib/passwordless/version.rb
82
- - lib/tasks/passwordless_tasks.rake
83
110
  homepage: https://github.com/mikker/passwordless
84
111
  licenses:
85
112
  - MIT
@@ -103,5 +130,5 @@ rubyforge_project:
103
130
  rubygems_version: 2.7.3
104
131
  signing_key:
105
132
  specification_version: 4
106
- summary: Summary of Passwordless.
133
+ summary: Add authentication to your app without all the ickyness of passwords.
107
134
  test_files: []
@@ -1,4 +0,0 @@
1
- # desc "Explaining what the task does"
2
- # task :passwordless do
3
- # # Task goes here
4
- # end