yellin 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +57 -0
- data/README.md +96 -0
- data/Rakefile +37 -0
- data/app/assets/config/yellin_manifest.js +2 -0
- data/app/assets/javascripts/yellin/account_activations.js +2 -0
- data/app/assets/javascripts/yellin/application.js +13 -0
- data/app/assets/javascripts/yellin/password_resets.js +2 -0
- data/app/assets/javascripts/yellin/registrations.js +2 -0
- data/app/assets/javascripts/yellin/sessions.js +2 -0
- data/app/assets/stylesheets/yellin/account_activations.css +4 -0
- data/app/assets/stylesheets/yellin/application.css +15 -0
- data/app/assets/stylesheets/yellin/password_resets.css +4 -0
- data/app/assets/stylesheets/yellin/registrations.css +4 -0
- data/app/assets/stylesheets/yellin/sessions.css +4 -0
- data/app/controllers/yellin/account_activations_controller.rb +24 -0
- data/app/controllers/yellin/application_controller.rb +7 -0
- data/app/controllers/yellin/password_resets_controller.rb +63 -0
- data/app/controllers/yellin/registrations_controller.rb +27 -0
- data/app/controllers/yellin/sessions_controller.rb +36 -0
- data/app/helpers/yellin/account_activations_helper.rb +4 -0
- data/app/helpers/yellin/application_helper.rb +4 -0
- data/app/helpers/yellin/password_resets_helper.rb +4 -0
- data/app/helpers/yellin/registrations_helper.rb +4 -0
- data/app/helpers/yellin/sessions_helper.rb +64 -0
- data/app/jobs/yellin/application_job.rb +4 -0
- data/app/mailers/yellin/application_mailer.rb +6 -0
- data/app/mailers/yellin/user_mailer.rb +23 -0
- data/app/models/yellin/application_record.rb +5 -0
- data/app/views/layouts/yellin/mailer.html.erb +13 -0
- data/app/views/layouts/yellin/mailer.text.erb +1 -0
- data/app/views/yellin/password_resets/edit.html.erb +14 -0
- data/app/views/yellin/password_resets/new.html.erb +9 -0
- data/app/views/yellin/registrations/_form.html.erb +14 -0
- data/app/views/yellin/registrations/new.html.erb +5 -0
- data/app/views/yellin/sessions/new.html.erb +18 -0
- data/app/views/yellin/shared/_error_messages.html.erb +12 -0
- data/app/views/yellin/user_mailer/account_activation.html.erb +9 -0
- data/app/views/yellin/user_mailer/account_activation.text.erb +5 -0
- data/app/views/yellin/user_mailer/password_reset.html.erb +9 -0
- data/app/views/yellin/user_mailer/password_reset.text.erb +9 -0
- data/config/routes.rb +11 -0
- data/lib/generators/active_record/templates/migration_create.rb +15 -0
- data/lib/generators/active_record/templates/migration_update.rb +27 -0
- data/lib/generators/active_record/yellin_generator.rb +47 -0
- data/lib/generators/yellin/USAGE +8 -0
- data/lib/generators/yellin/templates/initializer.rb +29 -0
- data/lib/generators/yellin/yellin_generator.rb +13 -0
- data/lib/tasks/yellin_tasks.rake +27 -0
- data/lib/yellin/acts_as_user.rb +87 -0
- data/lib/yellin/engine.rb +11 -0
- data/lib/yellin/version.rb +3 -0
- data/lib/yellin.rb +43 -0
- metadata +155 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 69ebc2ac5998feed166538df0c55de6a2e4bece7
|
4
|
+
data.tar.gz: 6ca97b96038a8039843d2c9151704bd7dc0ff29a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 371978b77a2a02cf8fc7290267b7d8faf7c68b8af8b0df61261de3f35297bf07d0f49198dfeeb6d0b70d6590322d4255682da1c36ac8ca0c2344df97d2e6b5d4
|
7
|
+
data.tar.gz: 6b12e489f519fe28545128f4d2d13ae6335babce08724b315c22e6f58afad07c5886a8796b5deb0b0fd9dddefe0b052baeb17db663571c6ba3f3043e9573d59d
|
data/LICENSE
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
This Engine is based on, and re-uses code from Michael Hartl's Ruby on Rails
|
2
|
+
Tutorial. The original code is licensed under the MIT and Beerware licenses
|
3
|
+
(see below). New modifications are similarly licensed under the MIT license:
|
4
|
+
|
5
|
+
=============================================================================
|
6
|
+
The MIT License
|
7
|
+
|
8
|
+
Copyright (c) 2017 Luke Hogan
|
9
|
+
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
12
|
+
in the Software without restriction, including without limitation the rights
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
15
|
+
furnished to do so, subject to the following conditions:
|
16
|
+
|
17
|
+
The above copyright notice and this permission notice shall be included in
|
18
|
+
all copies or substantial portions of the Software.
|
19
|
+
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
26
|
+
THE SOFTWARE.
|
27
|
+
|
28
|
+
|
29
|
+
Original licenses:
|
30
|
+
=============================================================================
|
31
|
+
Copyright 2016 Michael Hartl
|
32
|
+
|
33
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
34
|
+
a copy of this software and associated documentation files (the
|
35
|
+
"Software"), to deal in the Software without restriction, including
|
36
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
37
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
38
|
+
permit persons to whom the Software is furnished to do so, subject to
|
39
|
+
the following conditions:
|
40
|
+
|
41
|
+
The above copyright notice and this permission notice shall be
|
42
|
+
included in all copies or substantial portions of the Software.
|
43
|
+
|
44
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
45
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
46
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
47
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
48
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
49
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
50
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
51
|
+
|
52
|
+
=============================================================================
|
53
|
+
THE BEERWARE LICENSE (Revision 42)
|
54
|
+
|
55
|
+
Michael Hartl wrote this code. As long as you retain this notice you can do
|
56
|
+
whatever you want with this stuff. If we meet some day, and you think this
|
57
|
+
stuff is worth it, you can buy me a beer in return.
|
data/README.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# Yellin
|
2
|
+
Sometimes you need secure authentication for your Rails app, but a huge library like Devise is overkill, and implementing your own using `has_secure_password` involves a bunch of typing (especially the tests). This is what Yellin is for -- it's a small Rails engine that implements a simple but secure authorization mechanism using `has_secure_password`.
|
3
|
+
|
4
|
+
### Features
|
5
|
+
* User registration
|
6
|
+
* Account activation
|
7
|
+
* Session authentication
|
8
|
+
* Password reset
|
9
|
+
|
10
|
+
### Password Resets
|
11
|
+
Matasano (now part of NCC Group) considers automated password reset one of their 7 Deadly Web App Features. The implementation here takes into careful consideration the advice in [this discussion](https://news.ycombinator.com/item?id=5033513).
|
12
|
+
|
13
|
+
### Credit where credit is due
|
14
|
+
This gem is heavily based on the authentication system developed in Michael Hartl's [Ruby on Rails Tutorial](http://railstutorial.org). Basically, I've always liked most of that code, but I got tired of re-implementing my own modified version whenever I didn't want to use Devise. So I did the obvious thing and enginified it.
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
Yellin provides a generator that takes care of all configuration you need to use it. Run the generator with the name of the user model class you want to use. If the model already exists, it will be updated. Otherwise, it will be created from scratch.
|
18
|
+
|
19
|
+
```bash
|
20
|
+
$ rails generate yellin User
|
21
|
+
```
|
22
|
+
|
23
|
+
If you decide Yellin is lame and need to remove it, you can use the normal `destroy` command:
|
24
|
+
|
25
|
+
```bash
|
26
|
+
$ rails destroy yellin User
|
27
|
+
```
|
28
|
+
|
29
|
+
### Configuration options
|
30
|
+
The generator installs a generator to `config/initializers/yellin.rb`. It looks like this:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
Yellin.app_name = Default App Name
|
34
|
+
Yellin.default_from_address = 'noreply@example.com'
|
35
|
+
Yellin.minimum_password_length = 12
|
36
|
+
Yellin.reset_timeout_hours = 2
|
37
|
+
Yellin.user_class = "User"
|
38
|
+
```
|
39
|
+
|
40
|
+
The last line (`Yellin.user_class`) is absolutely necessary and will be set correctly based on the model to which you point the generator. *The quotes are essential.*
|
41
|
+
|
42
|
+
`Yellin.app_name` is used to identify your app in the emails sent to users when they sign up or reset their passwords. Set it accordingly. `Yellin.default_from_address` is the email address from which these emails will be sent.
|
43
|
+
|
44
|
+
In order to send emails, you must have ActionMailer configured correctly. For example, in development:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
# config/environments/development.rb
|
48
|
+
Rails.application.configure do
|
49
|
+
...
|
50
|
+
config.action_mailer.raise_delivery_errors = true
|
51
|
+
config.action_mailer.delivery_method = :test
|
52
|
+
host = 'localhost:3000'
|
53
|
+
config.action_mailer.default_url_options = { host: host, protocol: 'http' }
|
54
|
+
...
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
You also must have a root route defined in `routes.rb`, or Yellin will break in several places (probably along with lots of other things).
|
59
|
+
|
60
|
+
### Views and Forms
|
61
|
+
You'll likely want to modify the views and forms used by Yellin -- probably at least the user signup form. To encourage you to modify these views, Yellin provides a rake task to copy the default files into your workspace:
|
62
|
+
|
63
|
+
```bash
|
64
|
+
$ rails yellin:install:views
|
65
|
+
```
|
66
|
+
|
67
|
+
You'll find the files copied under `app/views/yellin`. Modify them in place -- if you move them, expect Yellin to misbehave.
|
68
|
+
|
69
|
+
If you change your mind, you can remove the view files like so:
|
70
|
+
|
71
|
+
```bash
|
72
|
+
$ rails yellin:uninstall:views
|
73
|
+
```
|
74
|
+
|
75
|
+
## Installation
|
76
|
+
Add this line to your application's Gemfile:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
gem 'yellin'
|
80
|
+
```
|
81
|
+
|
82
|
+
And then execute:
|
83
|
+
```bash
|
84
|
+
$ bundle install
|
85
|
+
```
|
86
|
+
|
87
|
+
Or install it yourself as:
|
88
|
+
```bash
|
89
|
+
$ gem install yellin
|
90
|
+
```
|
91
|
+
|
92
|
+
## Contributing
|
93
|
+
Bugfixes, enhancements, spelling corrections are all welcome. Use the pull request button :)
|
94
|
+
|
95
|
+
## License
|
96
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Yellin'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
load 'rails/tasks/statistics.rake'
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
require 'bundler/gem_tasks'
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
|
29
|
+
Rake::TestTask.new(:test) do |t|
|
30
|
+
t.libs << 'lib'
|
31
|
+
t.libs << 'test'
|
32
|
+
t.pattern = 'test/**/*_test.rb'
|
33
|
+
t.verbose = false
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
task default: :test
|
@@ -0,0 +1,13 @@
|
|
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 any plugin's vendor/assets/javascripts directory 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
|
+
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,15 @@
|
|
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 any plugin's vendor/assets/stylesheets directory 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 bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_dependency "yellin/application_controller"
|
2
|
+
|
3
|
+
module Yellin
|
4
|
+
class AccountActivationsController < ApplicationController
|
5
|
+
|
6
|
+
def edit
|
7
|
+
@user = Yellin.user_class.find_by(email: params[:email])
|
8
|
+
if @user && !@user.activated? && @user.authenticated?(:activation, params[:id])
|
9
|
+
@user.activate
|
10
|
+
log_in @user
|
11
|
+
flash[:success] = Yellin.flash[:activation_success]
|
12
|
+
begin
|
13
|
+
redirect_to @user
|
14
|
+
rescue NoMethodError
|
15
|
+
redirect_to main_app.root_url
|
16
|
+
end
|
17
|
+
else
|
18
|
+
flash[:danger] = Yellin.flash[:activation_invalid]
|
19
|
+
redirect_to main_app.root_url
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require_dependency "yellin/application_controller"
|
2
|
+
|
3
|
+
module Yellin
|
4
|
+
class PasswordResetsController < ApplicationController
|
5
|
+
before_action :get_user, only: [:edit, :update]
|
6
|
+
before_action :valid_user, only: [:edit, :update]
|
7
|
+
before_action :check_expiration, only: [:edit, :update]
|
8
|
+
|
9
|
+
def new
|
10
|
+
end
|
11
|
+
|
12
|
+
def create
|
13
|
+
@user = Yellin.user_class.find_by(email: params[:password_reset][:email].downcase)
|
14
|
+
if @user
|
15
|
+
@user.create_reset_digest
|
16
|
+
@user.send_password_reset_email
|
17
|
+
flash[:info] = Yellin.flash[:reset_sent]
|
18
|
+
redirect_to main_app.root_url
|
19
|
+
else
|
20
|
+
flash.now[:danger] = Yellin.flash[:reset_invalid]
|
21
|
+
render 'new'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def edit
|
26
|
+
end
|
27
|
+
|
28
|
+
def update
|
29
|
+
if params[:user][:password].empty?
|
30
|
+
@user.errors.add(:password, "can't be empty")
|
31
|
+
render 'edit'
|
32
|
+
elsif @user.reset_password(user_params)
|
33
|
+
flash[:success] = Yellin.flash[:reset_success]
|
34
|
+
redirect_to login_path
|
35
|
+
else
|
36
|
+
render 'edit'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def user_params
|
43
|
+
params.require(:user).permit(:password, :password_confirmation)
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_user
|
47
|
+
@user = Yellin.user_class.find_by(email: params[:email])
|
48
|
+
end
|
49
|
+
|
50
|
+
def valid_user
|
51
|
+
unless (@user && @user.activated? && @user.authenticated?(:reset, params[:id]))
|
52
|
+
redirect_to main_app.root_url
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def check_expiration
|
57
|
+
if @user.password_reset_expired?
|
58
|
+
flash[:danger] = Yellin.flash[:reset_expired]
|
59
|
+
redirect_to new_password_reset_url
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_dependency "yellin/application_controller"
|
2
|
+
|
3
|
+
module Yellin
|
4
|
+
class RegistrationsController < ApplicationController
|
5
|
+
|
6
|
+
def create
|
7
|
+
@user = Yellin.user_class.new(signup_params)
|
8
|
+
if @user.save
|
9
|
+
@user.send_activation_email
|
10
|
+
flash[:info] = Yellin.flash[:activation_pending]
|
11
|
+
redirect_to main_app.root_url
|
12
|
+
else
|
13
|
+
render 'new'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def new
|
18
|
+
@user = Yellin.user_class.new
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def signup_params
|
23
|
+
user_key = Yellin.user_class.model_name.param_key
|
24
|
+
params.require(user_key).permit(:email, :password, :password_confirmation)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require_dependency "yellin/application_controller"
|
2
|
+
|
3
|
+
module Yellin
|
4
|
+
class SessionsController < ApplicationController
|
5
|
+
|
6
|
+
def create
|
7
|
+
@user = Yellin.user_class.find_by(email: params[:session][:email].downcase)
|
8
|
+
if @user && @user.authenticate(params[:session][:password])
|
9
|
+
if @user.activated?
|
10
|
+
log_in(@user)
|
11
|
+
params[:session][:remember_me] == '1' ? remember(@user) : forget(@user)
|
12
|
+
begin
|
13
|
+
redirect_back_or @user
|
14
|
+
rescue NoMethodError
|
15
|
+
redirect_to main_app.root_url
|
16
|
+
end
|
17
|
+
else
|
18
|
+
flash[:warning] = Yellin.flash[:account_inactive]
|
19
|
+
redirect_to main_app.root_url
|
20
|
+
end
|
21
|
+
else
|
22
|
+
flash.now[:danger] = Yellin.flash[:bad_credentials]
|
23
|
+
render 'new'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def destroy
|
28
|
+
log_out if logged_in?
|
29
|
+
redirect_to main_app.root_url
|
30
|
+
end
|
31
|
+
|
32
|
+
def new
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Yellin
|
2
|
+
module SessionsHelper
|
3
|
+
|
4
|
+
def current_user
|
5
|
+
if (user_id = session[:user_id])
|
6
|
+
@current_user ||= Yellin.user_class.find_by(id: user_id)
|
7
|
+
elsif (user_id = cookies.signed[:user_id])
|
8
|
+
user = Yellin.user_class.find_by(id: user_id)
|
9
|
+
if user && user.authenticated?(:remember, cookies[:remember_token])
|
10
|
+
log_in user
|
11
|
+
@current_user = user
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def current_user?(user)
|
17
|
+
user == current_user
|
18
|
+
end
|
19
|
+
|
20
|
+
def log_in(user)
|
21
|
+
session[:user_id] = user.id
|
22
|
+
end
|
23
|
+
|
24
|
+
def log_out
|
25
|
+
forget(current_user)
|
26
|
+
session.delete(:user_id)
|
27
|
+
@current_user = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def logged_in?
|
31
|
+
!current_user.nil?
|
32
|
+
end
|
33
|
+
|
34
|
+
def remember(user)
|
35
|
+
user.remember
|
36
|
+
cookies.permanent.signed[:user_id] = user.id
|
37
|
+
cookies.permanent[:remember_token] = user.remember_token
|
38
|
+
end
|
39
|
+
|
40
|
+
def forget(user)
|
41
|
+
user.forget
|
42
|
+
cookies.delete(:user_id)
|
43
|
+
cookies.delete(:remember_token)
|
44
|
+
end
|
45
|
+
|
46
|
+
def store_location
|
47
|
+
session[:forwarding_url] = request.original_url if request.get?
|
48
|
+
end
|
49
|
+
|
50
|
+
def redirect_back_or(default)
|
51
|
+
redirect_to(session[:forwarding_url] || default)
|
52
|
+
session.delete(:forwarding_url)
|
53
|
+
end
|
54
|
+
|
55
|
+
def logged_in_user
|
56
|
+
unless logged_in?
|
57
|
+
store_location
|
58
|
+
flash[:danger] = "Please sign in."
|
59
|
+
redirect_to login_url
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Yellin
|
2
|
+
class UserMailer < ApplicationMailer
|
3
|
+
# Subject can be set in your I18n file at config/locales/en.yml
|
4
|
+
# with the following lookup:
|
5
|
+
#
|
6
|
+
# en.user_mailer.account_activation.subject
|
7
|
+
#
|
8
|
+
def account_activation(user)
|
9
|
+
@user = user
|
10
|
+
mail to: user.email, subject: 'Account activation'
|
11
|
+
end
|
12
|
+
|
13
|
+
# Subject can be set in your I18n file at config/locales/en.yml
|
14
|
+
# with the following lookup:
|
15
|
+
#
|
16
|
+
# en.user_mailer.password_reset.subject
|
17
|
+
#
|
18
|
+
def password_reset(user)
|
19
|
+
@user = user
|
20
|
+
mail to: user.email, subject: 'Password reset'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= yield %>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<% provide(:title, "Reset password") %>
|
2
|
+
<h1>Reset password</h1>
|
3
|
+
|
4
|
+
<%= form_for(@user, url: password_reset_path(params[:id])) do |f| %>
|
5
|
+
<%= render 'yellin/shared/error_messages', object: f.object %>
|
6
|
+
<%= hidden_field_tag :email, @user.email %>
|
7
|
+
<%= f.label :password %>
|
8
|
+
<%= f.password_field :password, class: 'form-control' %>
|
9
|
+
|
10
|
+
<%= f.label :password_confirmation, "Confirmation" %>
|
11
|
+
<%= f.password_field :password_confirmation, class: 'form-control' %>
|
12
|
+
|
13
|
+
<%= f.submit "Update password", class: "btn btn-primary" %>
|
14
|
+
<% end %>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<% provide(:title, "Forgot password") %>
|
2
|
+
|
3
|
+
<h1>Forgot password</h1>
|
4
|
+
<%= form_for(:password_reset, url: password_resets_path) do |f| %>
|
5
|
+
<%= f.label :email %>
|
6
|
+
<%= f.email_field :email, class: 'form-control' %>
|
7
|
+
|
8
|
+
<%= f.submit "Submit", class: "btn btn-primary" %>
|
9
|
+
<% end %>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<%= form_for(@user, url: yield(:form_action)) do |f| %>
|
2
|
+
<%= render 'yellin/shared/error_messages', object: f.object %>
|
3
|
+
|
4
|
+
<%= f.label :email %>
|
5
|
+
<%= f.email_field :email %>
|
6
|
+
|
7
|
+
<%= f.label :password %>
|
8
|
+
<%= f.password_field :password %>
|
9
|
+
|
10
|
+
<%= f.label :password_confirmation, "Confirm password" %>
|
11
|
+
<%= f.password_field :password_confirmation %>
|
12
|
+
|
13
|
+
<%= f.submit yield(:button_text), class: "btn btn-primary" %>
|
14
|
+
<% end %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<% provide(:title, "Sign in") %>
|
2
|
+
<h1>Sign in</h1>
|
3
|
+
<%= form_for(:session, url: login_path) do |f| %>
|
4
|
+
<%= f.label :email %>
|
5
|
+
<%= f.email_field :email %>
|
6
|
+
|
7
|
+
<%= f.label :password %>
|
8
|
+
<%= link_to "(forgot password)", new_password_reset_path %>
|
9
|
+
<%= f.password_field :password %>
|
10
|
+
|
11
|
+
<%= f.label :remember_me, class: "checkbox inline" do %>
|
12
|
+
<%= f.check_box :remember_me %>
|
13
|
+
<span>Remember me on this computer</span>
|
14
|
+
<% end %>
|
15
|
+
|
16
|
+
<%= f.submit "Sign in", class: "btn btn-primary" %>
|
17
|
+
<% end %>
|
18
|
+
<p>New user? <%= link_to "Sign up now", signup_path %></p>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<% if object.errors.any? %>
|
2
|
+
<div id="error_explanation">
|
3
|
+
<div class="alert alert-danger">
|
4
|
+
The form contains <%= pluralize(object.errors.count, "error") %>.
|
5
|
+
</div>
|
6
|
+
<ul>
|
7
|
+
<% object.errors.full_messages.each do |msg| %>
|
8
|
+
<li><%= msg %></li>
|
9
|
+
<% end %>
|
10
|
+
</ul>
|
11
|
+
</div>
|
12
|
+
<% end %>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<h1>Password reset</h1>
|
2
|
+
|
3
|
+
<p>To reset your password, click the link below:</p>
|
4
|
+
|
5
|
+
<%= link_to "Reset password", edit_password_reset_url(@user.reset_token, email: @user.email) %>
|
6
|
+
|
7
|
+
<p>This link will expire in two hours.</p>
|
8
|
+
|
9
|
+
<p>If you did not request your password to be reset, please ignore this message and your password will stay as it is.</p>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Password reset
|
2
|
+
|
3
|
+
To reset your password, click the link below:
|
4
|
+
|
5
|
+
<%= edit_password_reset_url(@user.reset_token, email: @user.email) %>
|
6
|
+
|
7
|
+
This link will expire in two hours.
|
8
|
+
|
9
|
+
If you did not request your password to be reset, please ignore this message and your password will stay as it is.
|
data/config/routes.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Yellin::Engine.routes.draw do
|
2
|
+
get 'signup', to: 'registrations#new'
|
3
|
+
post 'signup', to: 'registrations#create'
|
4
|
+
get 'login', to: 'sessions#new'
|
5
|
+
post 'login', to: 'sessions#create'
|
6
|
+
delete 'logout', to: 'sessions#destroy'
|
7
|
+
get 'password_resets/new'
|
8
|
+
get 'password_resets/edit'
|
9
|
+
resources :account_activations, only: [:edit]
|
10
|
+
resources :password_resets, only: [:create, :edit, :new, :update]
|
11
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
2
|
+
def change
|
3
|
+
create_table :<%= table_name %><%= primary_key_type %> do |t|
|
4
|
+
t.string :email
|
5
|
+
t.string :password_digest
|
6
|
+
t.string :remember_digest
|
7
|
+
t.string :activation_digest
|
8
|
+
t.datetime :activated_at
|
9
|
+
t.string :reset_digest
|
10
|
+
t.datetime :reset_sent_at
|
11
|
+
t.timestamps
|
12
|
+
t.index [:email], name: :index_users_on_email, unique: true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
2
|
+
def self.up
|
3
|
+
change_table :<%= table_name %> do |t|
|
4
|
+
t.string :email
|
5
|
+
t.string :password_digest
|
6
|
+
t.string :remember_digest
|
7
|
+
t.string :activation_digest
|
8
|
+
t.datetime :activated_at
|
9
|
+
t.string :reset_digest
|
10
|
+
t.datetime :reset_sent_at
|
11
|
+
t.index [:email], name: :index_users_on_email, unique: true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.down
|
16
|
+
raise ActiveRecord::IrreversibleMigration
|
17
|
+
## Uncomment and edit below to remove any fields that were in your original model and you want to keep
|
18
|
+
# remove_column :<%= table_name %>, :email, :string
|
19
|
+
# remove_column :<%= table_name %>, :password_digest, :string
|
20
|
+
# remove_column :<%= table_name %>, :remember_digest, :string
|
21
|
+
# remove_column :<%= table_name %>, :activation_digest, :string
|
22
|
+
# remove_column :<%= table_name %>, :activated_at, :datetime
|
23
|
+
# remove_column :<%= table_name %>, :reset_digest, :string
|
24
|
+
# remove_column :<%= table_name %>, :reset_sent_at, :datetime
|
25
|
+
# remove_index :<%= table_name %>, name: :index_users_on_email
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rails/generators/active_record'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Generators
|
5
|
+
class YellinGenerator < ActiveRecord::Generators::Base
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
7
|
+
|
8
|
+
def handle_migration
|
9
|
+
@preexisting_model = (behavior == :invoke && model_exists?) || (behavior == :revoke && migration_exists?)
|
10
|
+
if @preexisting_model
|
11
|
+
migration_template "migration_update.rb", "db/migrate/add_yellin_to_#{table_name}.rb"
|
12
|
+
else
|
13
|
+
migration_template "migration_create.rb", "db/migrate/yellin_create_#{table_name}.rb"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def handle_model
|
18
|
+
yellinize_model if behavior == :revoke
|
19
|
+
unless @preexisting_model
|
20
|
+
invoke "active_record:model", [name], migration: false
|
21
|
+
end
|
22
|
+
yellinize_model if behavior == :invoke
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def yellinize_model
|
27
|
+
# Avoid subtracting from non-existent file
|
28
|
+
if model_exists?
|
29
|
+
inject_into_file "app/models/#{file_name}.rb", after: "class #{class_name} < ApplicationRecord\n" do <<-YELLIN
|
30
|
+
include Yellin::ActsAsUser
|
31
|
+
acts_as_user
|
32
|
+
YELLIN
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def model_exists?
|
38
|
+
File.exists? File.join(destination_root, "app/models", "#{file_name}.rb")
|
39
|
+
end
|
40
|
+
|
41
|
+
def migration_exists?
|
42
|
+
pattern = File.join(destination_root, "db/migrate", "*_add_yellin_to_#{table_name}.rb")
|
43
|
+
Dir.glob(pattern).first
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# this is used to identify the application in account activation emails
|
2
|
+
Yellin.app_name = <%= Rails.application.class.parent %>
|
3
|
+
|
4
|
+
# this is used in the mailer to send account activation and password reset emails
|
5
|
+
Yellin.default_from_address = "noreply@example.com"
|
6
|
+
|
7
|
+
# this defines the notifications seen by the user
|
8
|
+
Yellin.flash = {
|
9
|
+
account_inactive: "Account not activated. Check your email for the activation link.",
|
10
|
+
activation_invalid: "Invalid activation link.",
|
11
|
+
activation_pending: "Please check your email to activate your account.",
|
12
|
+
activation_success: "Account activated.",
|
13
|
+
bad_credentials: "Invalid email/password combination.",
|
14
|
+
login_required: "Please sign in.",
|
15
|
+
reset_expired: "Password reset has expired.",
|
16
|
+
reset_invalid: "Email address not found.",
|
17
|
+
reset_sent: "Email sent with password reset instructions.",
|
18
|
+
reset_success: "Your password has been reset.",
|
19
|
+
}
|
20
|
+
|
21
|
+
# this defines the minimum acceptable length of a password
|
22
|
+
Yellin.password_minimum_length = 12
|
23
|
+
|
24
|
+
# this is the number of hours after which a password reset expires. Should be an integer.
|
25
|
+
Yellin.reset_timeout_hours = 2
|
26
|
+
|
27
|
+
# this is the name of the class implementing the User API
|
28
|
+
Yellin.user_class = "<%= name %>"
|
29
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class YellinGenerator < Rails::Generators::NamedBase
|
2
|
+
source_root File.expand_path('../templates', __FILE__)
|
3
|
+
|
4
|
+
def copy_initializer_file
|
5
|
+
template "initializer.rb", "config/initializers/yellin.rb"
|
6
|
+
end
|
7
|
+
|
8
|
+
def mount_engine
|
9
|
+
route "mount Yellin::Engine => '/'"
|
10
|
+
end
|
11
|
+
|
12
|
+
hook_for :orm
|
13
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
namespace :yellin do
|
4
|
+
namespace :install do
|
5
|
+
desc "Install Yellin views"
|
6
|
+
task :views do
|
7
|
+
source_root = Gem.loaded_specs['yellin'].full_gem_path
|
8
|
+
Dir.glob(File.join(source_root, "app/views/**/*.erb")).each do |view|
|
9
|
+
view.slice!(source_root)
|
10
|
+
path = view.split('/')
|
11
|
+
filename = path.pop
|
12
|
+
FileUtils.mkdir_p(File.join(Rails.root, path))
|
13
|
+
source = File.join(source_root, path, filename)
|
14
|
+
target = File.join(Rails.root, path, filename)
|
15
|
+
copy_file source, target
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
namespace :uninstall do
|
21
|
+
desc "Uninstall Yellin views"
|
22
|
+
task :views do
|
23
|
+
FileUtils.rm_rf(File.join(Rails.root, "app/views/yellin"))
|
24
|
+
FileUtils.rm_rf(File.join(Rails.root, "app/views/layouts/yellin"))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Yellin
|
2
|
+
module ActsAsUser
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def acts_as_user
|
10
|
+
attr_accessor :remember_token, :activation_token, :reset_token
|
11
|
+
has_secure_password
|
12
|
+
before_save :downcase_email
|
13
|
+
before_create :create_activation_digest
|
14
|
+
validates :password, presence: true, length: { minimum: Yellin.password_minimum_length }, allow_nil: true
|
15
|
+
# See https://davidcel.is/posts/stop-validating-email-addresses-with-regex/ for regex reasoning
|
16
|
+
validates :email, presence: true, length: { maximum: 255 }, format: { with: /@/ },
|
17
|
+
uniqueness: { case_sensitive: false }
|
18
|
+
|
19
|
+
def self.digest(string)
|
20
|
+
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
|
21
|
+
BCrypt::Password.create(string, cost: cost)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.new_token
|
25
|
+
SecureRandom.urlsafe_base64
|
26
|
+
end
|
27
|
+
|
28
|
+
include Yellin::ActsAsUser::LocalInstanceMethods
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module LocalInstanceMethods
|
33
|
+
def activate
|
34
|
+
update_attribute(:activated_at, Time.zone.now)
|
35
|
+
end
|
36
|
+
|
37
|
+
def activated?
|
38
|
+
!activated_at.nil? && activated_at < Time.zone.now
|
39
|
+
end
|
40
|
+
|
41
|
+
def authenticated?(attribute, token)
|
42
|
+
digest = send("#{attribute}_digest")
|
43
|
+
return false if digest.nil?
|
44
|
+
BCrypt::Password.new(digest).is_password?(token)
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_reset_digest
|
48
|
+
self.reset_token = Yellin.user_class.new_token
|
49
|
+
update_columns(reset_digest: Yellin.user_class.digest(reset_token), reset_sent_at: Time.zone.now)
|
50
|
+
end
|
51
|
+
|
52
|
+
def forget
|
53
|
+
update_attribute(:remember_digest, nil)
|
54
|
+
end
|
55
|
+
|
56
|
+
def password_reset_expired?
|
57
|
+
reset_sent_at < Yellin.reset_timeout_hours.hours.ago
|
58
|
+
end
|
59
|
+
|
60
|
+
def remember
|
61
|
+
self.remember_token = Yellin.user_class.new_token
|
62
|
+
update_attribute(:remember_digest, Yellin.user_class.digest(remember_token))
|
63
|
+
end
|
64
|
+
|
65
|
+
def reset_password(params)
|
66
|
+
update_attributes(password: params[:password], password_confirmation: params[:password_confirmation], reset_digest: nil)
|
67
|
+
end
|
68
|
+
|
69
|
+
def send_activation_email
|
70
|
+
UserMailer.account_activation(self).deliver_now
|
71
|
+
end
|
72
|
+
|
73
|
+
def send_password_reset_email
|
74
|
+
UserMailer.password_reset(self).deliver_now
|
75
|
+
end
|
76
|
+
|
77
|
+
def downcase_email
|
78
|
+
self.email.downcase!
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_activation_digest
|
82
|
+
self.activation_token = Yellin.user_class.new_token
|
83
|
+
self.activation_digest = Yellin.user_class.digest(activation_token)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/yellin.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require "yellin/engine"
|
2
|
+
require "yellin/acts_as_user"
|
3
|
+
|
4
|
+
module Yellin
|
5
|
+
mattr_accessor :app_name, :default_from_address, :flash, :password_minimum_length, :reset_timeout_hours, :user_class
|
6
|
+
|
7
|
+
def self.app_name
|
8
|
+
@@app_name || Rails.application.class.parent
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.default_from_address
|
12
|
+
@@default_from_address || 'noreply@exmaple.com'
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.flash
|
16
|
+
default_flash = {
|
17
|
+
account_inactive: "Account not activated. Check your email for the activation link.",
|
18
|
+
activation_invalid: "Invalid activation link.",
|
19
|
+
activation_pending: "Please check your email to activate your account.",
|
20
|
+
activation_success: "Account activated.",
|
21
|
+
bad_credentials: "Invalid email/password combination.",
|
22
|
+
login_required: "Please sign in.",
|
23
|
+
reset_expired: "Password reset has expired.",
|
24
|
+
reset_invalid: "Email address not found.",
|
25
|
+
reset_sent: "Email sent with password reset instructions.",
|
26
|
+
reset_success: "Your password has been reset.",
|
27
|
+
}
|
28
|
+
@@flash || default_flash
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.password_minimum_length
|
32
|
+
@@password_minimum_length || 12
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.reset_timeout_hours
|
36
|
+
@@reset_timeout_hours || 2
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.user_class
|
40
|
+
@@user_class.constantize
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yellin
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Luke Hogan
|
8
|
+
- Michael Hartl
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2017-04-10 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 5.0.2
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 5.0.2
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: bcrypt
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 3.1.11
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 3.1.11
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: sqlite3
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rails-controller-testing
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
description: Rails engine providing user authentication and basic account management
|
71
|
+
(account confirmation, password reset); Based on authentication framework developed
|
72
|
+
during Michael Hartl's Ruby on Rails Tutorial.
|
73
|
+
email:
|
74
|
+
- hoganld@gmail.com
|
75
|
+
executables: []
|
76
|
+
extensions: []
|
77
|
+
extra_rdoc_files: []
|
78
|
+
files:
|
79
|
+
- LICENSE
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- app/assets/config/yellin_manifest.js
|
83
|
+
- app/assets/javascripts/yellin/account_activations.js
|
84
|
+
- app/assets/javascripts/yellin/application.js
|
85
|
+
- app/assets/javascripts/yellin/password_resets.js
|
86
|
+
- app/assets/javascripts/yellin/registrations.js
|
87
|
+
- app/assets/javascripts/yellin/sessions.js
|
88
|
+
- app/assets/stylesheets/yellin/account_activations.css
|
89
|
+
- app/assets/stylesheets/yellin/application.css
|
90
|
+
- app/assets/stylesheets/yellin/password_resets.css
|
91
|
+
- app/assets/stylesheets/yellin/registrations.css
|
92
|
+
- app/assets/stylesheets/yellin/sessions.css
|
93
|
+
- app/controllers/yellin/account_activations_controller.rb
|
94
|
+
- app/controllers/yellin/application_controller.rb
|
95
|
+
- app/controllers/yellin/password_resets_controller.rb
|
96
|
+
- app/controllers/yellin/registrations_controller.rb
|
97
|
+
- app/controllers/yellin/sessions_controller.rb
|
98
|
+
- app/helpers/yellin/account_activations_helper.rb
|
99
|
+
- app/helpers/yellin/application_helper.rb
|
100
|
+
- app/helpers/yellin/password_resets_helper.rb
|
101
|
+
- app/helpers/yellin/registrations_helper.rb
|
102
|
+
- app/helpers/yellin/sessions_helper.rb
|
103
|
+
- app/jobs/yellin/application_job.rb
|
104
|
+
- app/mailers/yellin/application_mailer.rb
|
105
|
+
- app/mailers/yellin/user_mailer.rb
|
106
|
+
- app/models/yellin/application_record.rb
|
107
|
+
- app/views/layouts/yellin/mailer.html.erb
|
108
|
+
- app/views/layouts/yellin/mailer.text.erb
|
109
|
+
- app/views/yellin/password_resets/edit.html.erb
|
110
|
+
- app/views/yellin/password_resets/new.html.erb
|
111
|
+
- app/views/yellin/registrations/_form.html.erb
|
112
|
+
- app/views/yellin/registrations/new.html.erb
|
113
|
+
- app/views/yellin/sessions/new.html.erb
|
114
|
+
- app/views/yellin/shared/_error_messages.html.erb
|
115
|
+
- app/views/yellin/user_mailer/account_activation.html.erb
|
116
|
+
- app/views/yellin/user_mailer/account_activation.text.erb
|
117
|
+
- app/views/yellin/user_mailer/password_reset.html.erb
|
118
|
+
- app/views/yellin/user_mailer/password_reset.text.erb
|
119
|
+
- config/routes.rb
|
120
|
+
- lib/generators/active_record/templates/migration_create.rb
|
121
|
+
- lib/generators/active_record/templates/migration_update.rb
|
122
|
+
- lib/generators/active_record/yellin_generator.rb
|
123
|
+
- lib/generators/yellin/USAGE
|
124
|
+
- lib/generators/yellin/templates/initializer.rb
|
125
|
+
- lib/generators/yellin/yellin_generator.rb
|
126
|
+
- lib/tasks/yellin_tasks.rake
|
127
|
+
- lib/yellin.rb
|
128
|
+
- lib/yellin/acts_as_user.rb
|
129
|
+
- lib/yellin/engine.rb
|
130
|
+
- lib/yellin/version.rb
|
131
|
+
homepage: https://github.com/dukemagen/yellin.git
|
132
|
+
licenses:
|
133
|
+
- MIT
|
134
|
+
metadata: {}
|
135
|
+
post_install_message:
|
136
|
+
rdoc_options: []
|
137
|
+
require_paths:
|
138
|
+
- lib
|
139
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
145
|
+
requirements:
|
146
|
+
- - ">="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
requirements: []
|
150
|
+
rubyforge_project:
|
151
|
+
rubygems_version: 2.5.2
|
152
|
+
signing_key:
|
153
|
+
specification_version: 4
|
154
|
+
summary: Rails engine providing authentication based on `has_secure_password`.
|
155
|
+
test_files: []
|