auth_three 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4963b95a4a2af52430d8fd08e5326114dd16c119
4
+ data.tar.gz: d2d693276a15bc58ba76244e437af622210a63a7
5
+ SHA512:
6
+ metadata.gz: f55db84bea441008016308734d9dcb8bc5737aed7e9a8a97f4d26faec40f47012dadbc4add90160d8ad12275798ea999fceffa9ec54861f893d81614716951c5
7
+ data.tar.gz: 286250c6d441e0aafe3d1b1010eaf0534c6af327307d46034504983e2809264180072a78a292eae874be24aba67f6b7b59e0b62e7eeb73d2618a503bf6b892f9
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2017 Michael Chilton
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,44 @@
1
+ # AuthThree
2
+ This is a small gem that will generate the necessary files and configuration for
3
+ a simple, effective authentication pattern.
4
+
5
+ ## Usage
6
+ After installation, run `rails g auth_three:install`, and the generator will
7
+ separate the appropriate files and run the necessary database migrations.
8
+
9
+ There is also a setting for controller namespacing, which is intended to make namespaced api
10
+ controllers and routes. Add a `--namespace=api` argument to your generation command to generate
11
+ namespaced controllers and routes. Note, this option is intended to be used to make namespaced
12
+ api controllers, and so will output controller files and routes that by default will respond with JSON.
13
+ All output may be modified later.
14
+
15
+ This pattern requires BCrypt for securing passwords. Be sure to have it in your Gemfile and run `bundle install` if necessary.
16
+
17
+ A database is required to use this pattern and run the generator. Be sure to run bundle exec rake db:create
18
+ if one does not yet exist.
19
+
20
+
21
+ *It is recommended to run this generator at the start of a project to avoid conflicts with other content already present.*
22
+
23
+ ## Installation
24
+ Add this line to your application's Gemfile:
25
+
26
+ ```ruby
27
+ gem 'auth_three'
28
+ ```
29
+
30
+ And then execute:
31
+ ```bash
32
+ $ bundle
33
+ ```
34
+
35
+ Or install it yourself as:
36
+ ```bash
37
+ $ gem install auth_three
38
+ ```
39
+
40
+ ## Contributing
41
+ Contributions are always welcome.
42
+
43
+ ## License
44
+ 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,33 @@
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 = 'AuthThree'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+
33
+ task default: :test
@@ -0,0 +1,3 @@
1
+ module AuthThree
2
+ VERSION = '0.1.1'
3
+ end
data/lib/auth_three.rb ADDED
@@ -0,0 +1,10 @@
1
+ module AuthThree
2
+
3
+
4
+ require 'rails/generators'
5
+
6
+ require 'generators/orm_helpers'
7
+ require 'generators/controller_helpers'
8
+
9
+ require 'generators/install_generator'
10
+ end
@@ -0,0 +1,147 @@
1
+ module AuthThree
2
+ module Generators
3
+ module ControllerHelpers
4
+ def users_controller_contents
5
+ <<RUBY
6
+ def show
7
+ @user = User.find(params[:id])
8
+ render :show
9
+ end
10
+
11
+ def new
12
+ @user = User.new
13
+ render :new
14
+ end
15
+
16
+ def create
17
+ @user = User.new(user_params)
18
+
19
+ if @user.save
20
+ sign_in(@user)
21
+ redirect_to links_url
22
+ else
23
+ flash.now[:errors] = @user.errors.full_messages
24
+ render :new
25
+ end
26
+ end
27
+
28
+ def edit
29
+ @user = User.find(params[:id])
30
+ render :edit
31
+ end
32
+
33
+ def update
34
+ @user = User.find(params[:id])
35
+ if @user.update
36
+ # default will redirect to root_url, change as you wish
37
+ redirect_to root_url
38
+ else
39
+ flash.now[:errors] = @user.errors.full_messages
40
+ render :edit
41
+ end
42
+ end
43
+ RUBY
44
+ end
45
+
46
+ def sessions_controller_contents
47
+ <<RUBY
48
+ def new
49
+ render :new
50
+ end
51
+
52
+ def create
53
+ user = User.find_by_credentials(
54
+ params[:user][:username],
55
+ params[:user][:password]
56
+ )
57
+
58
+ if user
59
+ login!(user)
60
+ # requires root_url to be set in routes.rb
61
+ redirect_to root_url
62
+ else
63
+ flash.now[:errors] = ["Invalid username or password"]
64
+ render :new
65
+ end
66
+ end
67
+
68
+ def destroy
69
+ logout!
70
+ redirect_to new_session_url
71
+ end
72
+ RUBY
73
+ end
74
+
75
+ def namespace_sessions_contents(namespace_label)
76
+ <<RUBY
77
+ def create
78
+ @user = User.find_by_credentials(
79
+ params[:user][:username],
80
+ params[:user][:password]
81
+ )
82
+
83
+ if @user
84
+ login!(@user)
85
+ render :"#{namespace_label}/users/show"
86
+ else
87
+ render(json: ['Sorry, you entered an incorrect email address or password.'], status: 401)
88
+ end
89
+ end
90
+
91
+ def destroy
92
+ @user = current_user
93
+ if @user
94
+ logout!
95
+ render json: @user
96
+ else
97
+ render(json: ["You are not signed in"], status: 404)
98
+ end
99
+ end
100
+ RUBY
101
+ end
102
+
103
+ def namespace_users_contents(namespace_label)
104
+ <<RUBY
105
+ def create
106
+ @user = User.new(user_params)
107
+ if @user.save
108
+ login!(@user)
109
+ render :show
110
+ else
111
+ errors = @user.errors.full_messages
112
+ render(json: errors, status: 422)
113
+ end
114
+ end
115
+
116
+ def show
117
+ @user = User.find(params[:id])
118
+ render :show
119
+ end
120
+
121
+ def update
122
+ @user = User.find(params[:user][:id])
123
+ if @user.update(user_params)
124
+ render :show
125
+ else
126
+ errors = @user.errors.full_messages
127
+ render(json: errors, status: 422)
128
+ end
129
+ end
130
+ RUBY
131
+ end
132
+
133
+ def router_contents(namespace_label)
134
+ <<-RUBY
135
+
136
+ namespace :#{namespace_label}, defaults: {format: :json} do
137
+ resources :users
138
+ end
139
+ namespace :#{namespace_label}, defaults: {format: :json} do
140
+ resource :session
141
+ end
142
+ RUBY
143
+ end
144
+
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,109 @@
1
+ module AuthThree
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ include AuthThree::Generators::OrmHelpers
5
+ include AuthThree::Generators::ControllerHelpers
6
+ source_root File.expand_path("../../templates", __FILE__)
7
+ class_option :namespace, type: :string, description: "Namespace your controllers"
8
+
9
+ def generate_db_content
10
+ invoke "active_record:model", ['user'], migration: true
11
+ end
12
+
13
+ def inject_create_users_migration_content
14
+ content = migration_data
15
+ indent_depth = 0
16
+ content = content.split("\n").map { |line| " " * indent_depth + line } .join("\n") << "\n"
17
+ index_content = migration_index_data
18
+ require create_users_migration_file
19
+ insert_into_file(create_users_migration_file, content, after: ":users do |t|\n")
20
+ insert_into_file(create_users_migration_file, index_content, after: "t.timestamps\n end")
21
+ end
22
+
23
+ def migrate_users_data
24
+ rake "db:migrate"
25
+ end
26
+
27
+ def inject_user_model_content
28
+ content = model_contents
29
+
30
+ indent_depth = 0
31
+ content = content.split("\n").map { |line| " " * indent_depth + line } .join("\n") << "\n"
32
+
33
+ inject_into_class("#{model_path}/user.rb", User, content)
34
+ end
35
+
36
+ def copy_application_controller
37
+ copy_file(controller_source("application_controller.rb"), "#{File.join("app", "controllers")}/application_controller.rb", force: true)
38
+ end
39
+
40
+ def generate_user_controllers
41
+ controller_names = ['users', 'sessions']
42
+ controller_names.each do |name|
43
+ if options[:namespace]
44
+ generate "controller", "#{options[:namespace]}/#{name} --no-helper --no-assets"
45
+ content = send("namespace_#{name}_contents".to_sym, options[:namespace])
46
+ indent_depth = 0
47
+ content = content.split("\n").map { |line| " " * indent_depth + line } .join("\n") << "\n"
48
+ inject_into_class("#{controllers_path}/#{name}_controller.rb", ("#{options[:namespace]}::".camelcase + "#{name}_controller".camelcase).constantize, content)
49
+ else
50
+ generate "controller", "#{name} --no-helper --no-assets"
51
+ content = send("#{name}_controller_contents".to_sym)
52
+ indent_depth = 0
53
+ content = content.split("\n").map { |line| " " * indent_depth + line } .join("\n") << "\n"
54
+ inject_into_class("#{controllers_path}/#{name}_controller.rb", "#{name}_controller".camelcase.constantize, content)
55
+ end
56
+ end
57
+ end
58
+
59
+ def generate_routes
60
+ if options[:namespace]
61
+ content = router_contents(options[:namespace])
62
+ indent_depth = 0
63
+ content = content.split("\n").map { |line| " " * indent_depth + line } .join("\n") << "\n"
64
+
65
+ route(content)
66
+ else
67
+ route "resources :users"
68
+ route "resource :session"
69
+ end
70
+ end
71
+
72
+
73
+ def further_instructions
74
+ readme "README.md"
75
+ end
76
+ private
77
+
78
+ def controllers_path
79
+ if options[:namespace]
80
+ File.join("app", "controllers", "#{options[:namespace]}")
81
+ else
82
+ File.join("app", "controllers")
83
+ end
84
+ end
85
+
86
+ def controller_source(controller_name)
87
+ if options[:namespace]
88
+ File.expand_path("../../templates/json_controllers/#{controller_name}", __FILE__)
89
+ else
90
+ File.expand_path("../../templates/html_controllers/#{controller_name}", __FILE__)
91
+ end
92
+ end
93
+
94
+ def migration_path
95
+ @migration_path ||= File.join("db", "migrate")
96
+ end
97
+
98
+ def create_users_migration_file
99
+ Dir.glob("#{File.join(destination_root, migration_path)}/[0-9]*_*.rb").grep(/\d+_create_users.rb$/).first
100
+ end
101
+
102
+ def model_path
103
+ @model_path ||= File.join("app", "models")
104
+ end
105
+
106
+
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,96 @@
1
+ module AuthThree
2
+ module Generators
3
+ module OrmHelpers
4
+
5
+ def model_contents
6
+ <<RUBY
7
+ validates :username, :email, :session_token, presence: true
8
+ validates :username, uniqueness: true
9
+ validates :password_digest, presence: { message: "Password can't be blank."}
10
+ validates :password, length: { minimum: 6, allow_nil: true }
11
+
12
+ after_initialize :ensure_session_token
13
+
14
+ def self.token
15
+ SecureRandom::urlsafe_base64(16)
16
+ end
17
+
18
+ def self.find_by_credentials(username, password)
19
+ user = User.find_by(username: username)
20
+ return nil if user.nil?
21
+ if user.is_password?(password)
22
+ return user
23
+ else
24
+ return nil
25
+ end
26
+ end
27
+
28
+ attr_reader :password
29
+
30
+ def password=(password)
31
+ @password = password
32
+ self.password_digest = digest(password)
33
+ end
34
+
35
+ def digest(string)
36
+ string_digest = BCrypt::Password.create(string)
37
+ end
38
+
39
+ def is_password?(password)
40
+ BCrypt::Password.new(self.password_digest).is_password?(password)
41
+ end
42
+
43
+ def reset_session_token!
44
+ self.session_token = User.token
45
+ self.save
46
+ self.session_token
47
+ end
48
+
49
+ private
50
+
51
+ def ensure_session_token
52
+ self.session_token ||= User.token
53
+ end
54
+ RUBY
55
+ end
56
+
57
+ def migration_data
58
+ <<RUBY
59
+ t.string :username, null: false
60
+
61
+ t.string :email, null: false
62
+
63
+ t.string :password_digest, null: false
64
+ t.string :session_token, null: false
65
+ RUBY
66
+ end
67
+
68
+ def migration_index_data
69
+ <<RUBY
70
+
71
+ add_index :users, :username, unique: true
72
+ add_index :users, :email, unique: true
73
+ add_index :users, :session_token, unique: true
74
+ RUBY
75
+ end
76
+
77
+ private
78
+
79
+ def model_exists?
80
+ File.exist?(File.join(destination_root, model_path))
81
+ end
82
+
83
+ def migration_exists?(table_name)
84
+ Dir.glob("#{File.join(destination_root, migration_path)}/[0-9]*_*.rb").grep(/\d+_create_users.rb$/).first
85
+ end
86
+
87
+ def migration_path
88
+ @migration_path ||= File.join("db", "migrate")
89
+ end
90
+
91
+ def model_path
92
+ @model_path ||= File.join("app", "models", "#{file_path}.rb")
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :auth_three do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,19 @@
1
+ ===============================================================================
2
+
3
+ Some setup you must do manually if you haven't yet:
4
+
5
+ 1. This authentication pattern depends on the BCrypt library for securing
6
+ passwords. If you have not done so already, uncomment or add in
7
+
8
+ gem 'bcrypt'
9
+
10
+ and then run
11
+
12
+ bundle install
13
+
14
+ 2. Ensure you have defined root_url to *something* in your config/routes.rb.
15
+ For example:
16
+
17
+ root to: "home#index"
18
+
19
+ ===============================================================================
@@ -0,0 +1,42 @@
1
+ class ApplicationController < ActionController::Base
2
+ protect_from_forgery with: :exception
3
+
4
+ helper_method :current_user, :logged_in?
5
+
6
+ def current_user
7
+ @current_user ||= User.find_by(session_token: session[:session_token])
8
+ end
9
+
10
+ def login!(user)
11
+ @current_user = user
12
+ session[:session_token] = user.reset_session_token!
13
+ end
14
+
15
+ def logout!
16
+ current_user.reset_session_token!
17
+ session[:session_token] = nil
18
+ end
19
+
20
+ def logged_in?
21
+ !!current_user
22
+ end
23
+
24
+ private
25
+
26
+ def require_logged_in
27
+ unless logged_in?
28
+ redirect_to new_session_url
29
+ end
30
+ end
31
+
32
+ def require_logged_out
33
+ if logged_in?
34
+ # redirect to a root of your choosing if users are in a place that should
35
+ # not be accessible when logged in
36
+ end
37
+ end
38
+
39
+ def user_params
40
+ params.require(:user).permit(:username, :password, :email)
41
+ end
42
+ end
@@ -0,0 +1,42 @@
1
+ class ApplicationController < ActionController::Base
2
+ protect_from_forgery with: :exception
3
+
4
+ helper_method :current_user, :logged_in?
5
+
6
+ def current_user
7
+ @current_user ||= User.find_by(session_token: session[:session_token])
8
+ end
9
+
10
+ def login!(user)
11
+ @current_user = user
12
+ session[:session_token] = user.reset_session_token!
13
+ end
14
+
15
+ def logout!
16
+ current_user.reset_session_token!
17
+ session[:session_token] = nil
18
+ end
19
+
20
+ def logged_in?
21
+ !!current_user
22
+ end
23
+
24
+ private
25
+
26
+ def require_logged_in
27
+ unless logged_in?
28
+ render json: { errors: ['Not signed in.'] }
29
+ end
30
+ end
31
+
32
+ def require_logged_out
33
+ if logged_in?
34
+ # redirect to a root of your choosing if users are in a place that should
35
+ # not be accessible when logged in
36
+ end
37
+ end
38
+
39
+ def user_params
40
+ params.require(:user).permit(:username, :password, :email)
41
+ end
42
+ end
@@ -0,0 +1,50 @@
1
+ class User < ApplicationRecord
2
+ validates :username, :email, :session_token, presence: true
3
+ validates :username, uniqueness: true
4
+ validates :password_digest, presence: { message: "Password can't be blank."}
5
+ validates :password, length: { minimum: 6, allow_nil: true }
6
+
7
+ after_initialize :ensure_session_token
8
+
9
+ def self.token
10
+ SecureRandom::urlsafe_base64(16)
11
+ end
12
+
13
+ def self.find_by_credentials(username, password)
14
+ user = User.find_by(username: username)
15
+ return nil if user.nil?
16
+ if user.is_password?(password)
17
+ return user
18
+ else
19
+ return nil
20
+ end
21
+ end
22
+
23
+ attr_reader :password
24
+
25
+ def password=(password)
26
+ @password = password
27
+ self.password_digest = digest(password)
28
+ end
29
+
30
+ def digest(string)
31
+ string_digest = BCrypt::Password.create(string)
32
+ end
33
+
34
+ def is_password?(password)
35
+ BCrypt::Password.new(self.password_digest).is_password?(password)
36
+ end
37
+
38
+ def reset_session_token!
39
+ self.session_token = User.token
40
+ self.save
41
+ self.session_token
42
+ end
43
+
44
+ private
45
+
46
+ def ensure_session_token
47
+ self.session_token ||= User.token
48
+ end
49
+
50
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: auth_three
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Michael Chilton
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-05-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 5.1.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 5.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: sqlite3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pg
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: byebug
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: pry
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: Automate basic authentication for your coding challenges
84
+ email:
85
+ - michaelc962@yahoo.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - MIT-LICENSE
91
+ - README.md
92
+ - Rakefile
93
+ - lib/auth_three.rb
94
+ - lib/auth_three/version.rb
95
+ - lib/generators/controller_helpers.rb
96
+ - lib/generators/install_generator.rb
97
+ - lib/generators/orm_helpers.rb
98
+ - lib/tasks/auth_three_tasks.rake
99
+ - lib/templates/README.md
100
+ - lib/templates/html_controllers/application_controller.rb
101
+ - lib/templates/json_controllers/application_controller.rb
102
+ - lib/templates/user.rb
103
+ homepage: https://github.com/mc962/auth_three
104
+ licenses:
105
+ - MIT
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 2.5.2
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: Automation for a basic authentication pattern
127
+ test_files: []