Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ GEM
bootstrap-sass (3.4.1)
autoprefixer-rails (>= 5.2.1)
sassc (>= 2.0.0)
brakeman (7.1.0)
brakeman (8.0.1)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For CI to pass

racc
builder (3.3.0)
capybara (3.40.0)
Expand Down
7 changes: 7 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@ class ApplicationController < ActionController::Base
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
allow_browser versions: :modern
before_action :check_session_expiry
before_action :check_user_token
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure user is authenticated in all controllers that inherit from ApplicationController


private

def check_user_token
unless session[:user_token]
render "puzzles/login"
end
end

def check_session_expiry
if session[:expires_at].present? && Time.current > session[:expires_at]
reset_session
Expand Down
13 changes: 13 additions & 0 deletions app/controllers/puzzles/clones_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class Puzzles::ClonesController < ApplicationController
def create
original_puzzle = Puzzle.find(params[:puzzle_id])
attributes = original_puzzle.attributes.slice("question", "answer", "explanation", "link", "suggested_by")
cloned_puzzle = Puzzle.new(attributes.merge(original_puzzle:, state: params.fetch(:state, "pending")))

if cloned_puzzle.save
redirect_to puzzles_path, notice: "Puzzle cloned. You can now edit the new puzzle."
else
redirect_to puzzles_path, alert: "Failed to clone puzzle."
end
end
end
4 changes: 0 additions & 4 deletions app/controllers/puzzles_controller.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
class PuzzlesController < ApplicationController
def index
unless session[:user_token]
render "login"
end

@pending_puzzles = Puzzle.pending
@approved_puzzles = Puzzle.approved
@rejected_puzzles = Puzzle.rejected
Expand Down
4 changes: 3 additions & 1 deletion app/controllers/sessions_controller.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
class SessionsController < ApplicationController
skip_before_action :check_user_token

def create
auth = request.env["omniauth.auth"]
user_email = auth.info.email

domain_allowlist = ENV.fetch("DOMAIN_ALLOWLIST").split(",").map(&:strip)
domain_allowlist = ENV.fetch("DOMAIN_ALLOWLIST", "").split(",").map(&:strip)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix a CI failure where DOMAIN_ALLOWLIST is not defined.

if domain_allowlist.present?
unless domain_allowlist.any? { |domain| user_email.end_with?("@#{domain}") }
reset_session
Expand Down
2 changes: 2 additions & 0 deletions app/controllers/slack/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
class Slack::ApplicationController < ApplicationController
skip_before_action :verify_authenticity_token
skip_before_action :check_user_token
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slack controllers don't relay on omniauth credentials.


before_action :valid_slack_request?

private
Expand Down
2 changes: 2 additions & 0 deletions app/models/puzzle.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ class Puzzle < ApplicationRecord
enum :state, { approved: 0, rejected: 1, pending: 2, archived: 3 }
has_many :answers

belongs_to :original_puzzle, class_name: "Puzzle", optional: true

validates :question, presence: true

scope :archived, -> { where(state: :archived).order(sent_at: :desc) }
Expand Down
1 change: 1 addition & 0 deletions app/views/puzzles/_puzzles_table.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
<%= button_to 'Pending', puzzle_state_path(puzzle, state: :pending), method: :patch, form_class: 'inline-form', class: 'btn pending-btn' %>
<% elsif actions == :archived %>
<%= button_to 'Un-Archive', puzzle_state_path(puzzle, state: :pending), method: :patch, form_class: 'inline-form', class: 'btn pending-btn' %>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to keep the Un-Archive button?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should since clone is being added, so we do not have to use un-archive.

<%= button_to 'Clone', puzzle_clone_path(puzzle, state: :pending), method: :post, form_class: 'inline-form', class: 'btn pending-btn' %>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cloned puzzled state can be set here.

<% end %>
</td>
</tr>
Expand Down
5 changes: 5 additions & 0 deletions config/environments/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,9 @@

# Raise error when a before_action's only/except options reference missing actions.
config.action_controller.raise_on_missing_callback_actions = true

# Once you have enabled test mode, all requests to OmniAuth will be short circuited to use
# the mock authentication hash.
# See: https://github.com/omniauth/omniauth/wiki/Integration-Testing
OmniAuth.config.test_mode = true
end
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Rails.application.routes.draw do
resources :puzzles, only: [ :index, :edit, :update ] do
resource :state, only: [ :update ], module: :puzzles
resource :clone, only: [ :create ], module: :puzzles
end
resources :sessions, only: [ :create, :destroy ]

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddOriginalPuzzleToPuzzlesTable < ActiveRecord::Migration[8.0]
def change
add_reference :puzzles, :original_puzzle, foreign_key: { to_table: :puzzles }, index: true, null: true
end
end
5 changes: 4 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions test/controllers/puzzles/clones_controller_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require "test_helper"

class Puzzles::ClonesControllerTest < ActionDispatch::IntegrationTest
test "creates a cloned puzzle and redirects to index" do
original = puzzles(:one)

sign_in
assert_difference("Puzzle.count", 1) do
post puzzle_clone_path(original)
end

cloned = Puzzle.order(:id).last
assert_redirected_to puzzles_path

assert_equal original.id, cloned.original_puzzle_id
assert_equal original.question, cloned.question
assert_equal original.answer, cloned.answer
assert_equal original.explanation, cloned.explanation
assert_equal original.link, cloned.link
assert_equal original.suggested_by, cloned.suggested_by
assert_nil cloned.sent_at
assert_equal "pending", cloned.state
end

test "does not allow unauthenticated users to create a clone" do
original = puzzles(:one)

assert_no_difference("Puzzle.count") do
post puzzle_clone_path(original)
end

assert_dom "p", "Log in to access the Ruby or Rails admin panel."
end
end
3 changes: 3 additions & 0 deletions test/controllers/puzzles_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

class PuzzlesControllerTest < ActionDispatch::IntegrationTest
test "should get index" do
sign_in
get puzzles_path
assert_response :success
end

test "should show error message when editing puzzle with invalid data" do
puzzle = puzzles(:one)

sign_in
patch puzzle_path(puzzle), params: {
puzzle: {
question: "",
Expand All @@ -27,6 +29,7 @@ class PuzzlesControllerTest < ActionDispatch::IntegrationTest
test "should successfully update puzzle with valid data" do
puzzle = puzzles(:one)

sign_in
patch puzzle_path(puzzle), params: {
puzzle: {
question: "Updated question",
Expand Down
22 changes: 22 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,28 @@ class TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all

# Global setup to be run before each test
setup do
OmniAuth.config.mock_auth[:google] = nil
end

# Add more helper methods to be used by all tests here...
def sign_in
auth = {
provider: "google_oauth2",
uid: "123456789",
info: {
email: "cooper@ombulabs.com"
},
credentials: {
token: "token"
}
}

OmniAuth.config.add_mock(:google, auth)
Rails.application.env_config["omniauth.auth"] = OmniAuth.config.mock_auth[:google]

post sessions_path, params: { provider: :google }
end
end
end