Last active
July 4, 2022 04:43
Helper scripts for testing devise-two-factor 4.x to 5.x
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# common helper functions | |
def replace_line(file_path:, old_line_regex:, new_line:) | |
new_lines = File.readlines(file_path, chomp: true).map do |line| | |
if old_line_regex.match?(line) | |
new_line | |
else | |
line | |
end | |
end | |
File.write(file_path, new_lines.join("\n")) | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require_relative "./lib" | |
def main | |
app_dir = ARGV.shift | |
Dir.chdir(app_dir) do | |
puts "Operating in: #{Dir.pwd}" | |
# start upgrade to Rails 7 | |
replace_line(file_path: "Gemfile", old_line_regex: /^gem 'rails'/, new_line: "gem 'rails', '~> 7.0'") | |
system "bundle update rails" | |
# update devise-two-factor to a version which supports Rails 7 | |
# (must be done because app:update won't run because old devise-two-factor breaks under Rails 7) | |
replace_line( | |
file_path: "Gemfile", | |
old_line_regex: /devise-two-factor/, | |
new_line: 'gem "devise-two-factor", path: "/Users/eoinkelly/Code/repos/devise-two-factor/devise-two-factor"' | |
) | |
# run Rails app:update script and any migrations it creates | |
system "yes | ./bin/rails app:update" | |
system "./bin/rails db:migrate" | |
# Create migration to add the Rails 7 encrypted attribute to the user model | |
# TODO: change name of model as required | |
system "./bin/rails g migration AddOtpSecretToUser otp_secret:string" | |
system "./bin/rails db:migrate" | |
# create rake task to migrate secret storage | |
File.write("lib/tasks/devise_two_factor.rake", <<~EOF | |
namespace :devise_two_factor do | |
desc "Copy devise_two_factor OTP secret from old format to new format" | |
task copy_otp_secret_to_rails7_encrypted_attr: [:environment] do | |
User.find_each do |user| | |
otp_secret = user.legacy_otp_secret | |
puts "Processing #{user.email}" | |
user.update!(otp_secret: otp_secret) | |
end | |
end | |
end | |
EOF | |
) | |
# setup rails encyrpted secrets | |
system "./bin/rails db:encryption:init" | |
# the rest has to be done interactively | |
# ./bin/rails credentials:edit | |
# Cleanup phase | |
# ############# | |
# | |
# create migration to remove the old columns | |
# ./bin/rails g migration RemoveLegacyDeviseTwoFactorSecretsFromUsers | |
# | |
# class RemoveLegacyDeviseTwoFactorSecretsFromUsers < ActiveRecord::Migration[7.0] | |
# def change | |
# remove_column :users, :encrypted_otp_secret | |
# remove_column :users, :encrypted_otp_secret_iv | |
# remove_column :users, :encrypted_otp_secret_salt | |
# end | |
# end | |
end | |
end | |
main |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require_relative "./lib" | |
## | |
# Create a new Rails (6 or 7) app ready to be have devise-two-factor upgraded | |
# | |
# Usage | |
# | |
# $ ./mk_r6_app | |
# | |
rails_version = "_6.1.4.6_" | |
app_name = "app_rails_#{rails_version == "" ? "7" : "6"}_devise_#{Time.zone.now.strftime("%Y_%m_%d__%H_%M_%S")}" | |
system "rails #{rails_version} --version" | |
system "rails #{rails_version} new --no-rc #{app_name}" | |
Dir.chdir(app_name) do | |
system "pwd" | |
system "git add . && git commit -m 'rails #{rails_version} new'" | |
system "bundle add pry-rails pry-byebug" | |
system "git add . && git commit -m 'Add handy gems'" | |
system "bundle exec rails g scaffold User name:string" | |
system "bundle exec rails db:create db:migrate" | |
system "git add . && git commit -m 'Create user via scaffold'" | |
system "bundle add devise" | |
system "bundle exec rails g devise:install " | |
system "bundle exec rails g devise User" | |
system "bundle exec rails db:migrate" | |
system "bundle exec rails g devise:views" # need views to modify for 2fa | |
# We want to be able to sign-out without faffing around with JS | |
replace_line( | |
file_path: "config/initializers/devise.rb", | |
old_line_regex: /sign_out_via/, | |
new_line: "config.sign_out_via = :get" | |
) | |
File.write("", <<~EOM | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Example devise-two-factor app</title> | |
<meta name="viewport" content="width=device-width,initial-scale=1"> | |
<%= csrf_meta_tags %> | |
<%= csp_meta_tag %> | |
<%= stylesheet_link_tag 'application', media: 'all' %> | |
</head> | |
<body> | |
<%= link_to("Sign out", destroy_user_session_path) if current_user %> | |
<%= yield %> | |
</body> | |
</html> | |
EOM | |
) | |
system "git add . && git commit -m 'Set up devise'" | |
system %q(echo 'gem "dotenv-rails", require: "dotenv/rails-now"' >> Gemfile) | |
system "bundle install" | |
system "bundle add devise-two-factor" | |
system "bundle exec rails g devise_two_factor user LEGACY_ENCRYPTION_KEY" | |
system "bundle exec rails db:migrate" | |
File.write("app/controllers/application_controller.rb", <<~EOM | |
class ApplicationController < ActionController::Base | |
before_action :configure_permitted_parameters, if: :devise_controller? | |
protected | |
def configure_permitted_parameters | |
devise_parameter_sanitizer.permit(:sign_in, keys: [:otp_attempt]) | |
end | |
end | |
EOM | |
) | |
File.write("config/routes.rb", <<~EOF | |
Rails.application.routes.draw do | |
devise_for :users | |
devise_scope :user do | |
authenticated :user do | |
resources :users | |
root to: "users#index" | |
end | |
unauthenticated do | |
root "devise/sessions#new", as: :unauthenticated_root | |
end | |
end | |
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html | |
end | |
EOF | |
) | |
File.write("app/views/devise/sessions/new.html.erb", <<~EOF | |
<h2>Log in</h2> | |
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> | |
<div class="field"> | |
<%= f.label :email %><br /> | |
<%= f.email_field :email, autofocus: true, autocomplete: "email" %> | |
</div> | |
<div class="field"> | |
<%= f.label :password %><br /> | |
<%= f.password_field :password, autocomplete: "current-password" %> | |
</div> | |
<% if devise_mapping.rememberable? %> | |
<div class="field"> | |
<%= f.check_box :remember_me %> | |
<%= f.label :remember_me %> | |
</div> | |
<% end %> | |
<div class="form__field"> | |
<%= f.label :otp_attempt, "2FA (Two Factor Auth) code", class: "form__label" %><br /> | |
<%= f.text_field :otp_attempt, class: "form__input" %> | |
</div> | |
<div class="actions"> | |
<%= f.submit "Log in" %> | |
</div> | |
<% end %> | |
<%= render "devise/shared/links" %> | |
EOF | |
) | |
File.write(".env", <<~EOM | |
LEGACY_ENCRYPTION_KEY=68d7574e6b971373fe4cece66edbd76aca11b09057b4b44a0785ee8435d09607bef9c2e0ed6a6a6d9ebfa7e53411a2433c5dc509ca365026670b0d638ddeddc6 | |
EOM | |
) | |
system "git add . && git commit -m 'Setup devise-two-factor'" | |
seed = <<~'EOM' | |
10.times do |n| | |
user = User.find_or_create_by(email: "foo-#{n}@example.com") do |user| | |
puts "Creating User: #{user.email}" | |
user.password = "blahblahblah" | |
user.name = "John #{n}Doe" | |
user.otp_secret = User.generate_otp_secret | |
user.otp_required_for_login = true | |
end | |
end | |
EOM | |
File.write("db/seeds.rb", seed) | |
system "bundle exec rails db:seed" | |
system "git add . && git commit -m 'Add DB Seeds'" | |
end | |
puts "Created '#{app_name}'" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment