Skip to content

Commit

Permalink
Merge pull request #3 from mitre-cyber-academy/scoreboardTweaks2015
Browse files Browse the repository at this point in the history
Update the scoreboard with new functionality needed for 2015 CTF
  • Loading branch information
Bialogs committed Aug 24, 2015
2 parents 18f3028 + c3b37d0 commit c6a96ce
Show file tree
Hide file tree
Showing 24 changed files with 377 additions and 53 deletions.
5 changes: 5 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ group :development do
gem 'sqlite3'
end

group :test do
gem "rspec-rails"
gem 'factory_girl_rails'
end

group :assets do
gem 'sass-rails'
gem 'coffee-rails'
Expand Down
25 changes: 25 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,14 @@ GEM
responders
thread_safe (~> 0.1)
warden (~> 1.2.3)
diff-lcs (1.2.5)
erubis (2.7.0)
execjs (2.5.2)
factory_girl (4.5.0)
activesupport (>= 3.0.0)
factory_girl_rails (4.5.0)
factory_girl (~> 4.5.0)
railties (>= 3.0.0)
font-awesome-rails (4.3.0.0)
railties (>= 3.2, < 5.0)
geocoder (1.2.8)
Expand Down Expand Up @@ -146,6 +152,23 @@ GEM
remotipart (1.2.1)
responders (2.1.0)
railties (>= 4.2.0, < 5)
rspec-core (3.3.2)
rspec-support (~> 3.3.0)
rspec-expectations (3.3.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.3.0)
rspec-mocks (3.3.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.3.0)
rspec-rails (3.3.3)
actionpack (>= 3.0, < 4.3)
activesupport (>= 3.0, < 4.3)
railties (>= 3.0, < 4.3)
rspec-core (~> 3.3.0)
rspec-expectations (~> 3.3.0)
rspec-mocks (~> 3.3.0)
rspec-support (~> 3.3.0)
rspec-support (3.3.0)
safe_yaml (1.0.4)
sass (3.4.13)
sass-rails (5.0.3)
Expand Down Expand Up @@ -183,6 +206,7 @@ DEPENDENCIES
bluecloth
coffee-rails
devise
factory_girl_rails
geocoder
haml
jbuilder
Expand All @@ -192,6 +216,7 @@ DEPENDENCIES
pg
rails
rails_admin
rspec-rails
sass-rails
sqlite3
therubyracer
Expand Down
20 changes: 20 additions & 0 deletions app/assets/stylesheets/challenges.css.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
// Place all the styles related to the challenges controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

.embed-container {
position: relative;
padding-bottom: 56.25%;
padding-top: 30px;
height: 0;
overflow: hidden;
width: 100%;
height: auto;
margin-bottom: 12px;
}

.embed-container iframe,
.embed-container object,
.embed-container embed {
position: absolute;
top: 0; left: 0;
width: 100%;
height: 100%;
}
30 changes: 26 additions & 4 deletions app/controllers/challenges_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,15 @@ def index
end

def show
is_admin = current_user.is_a?(Admin)
# Accept the flag via GET request if the current user is an admin.
@admin_flag = Flag.find(params[:flag]) if is_admin && params[:flag]
@solved = @challenge.is_solved_by_user?(current_user)
@solved_video_url = @challenge.get_video_url_for_flag(current_user)
# Get video URL for admins
@solved_video_url = @admin_flag.video_url if @admin_flag
@solved_by = SolvedChallenge.where("challenge_id = :challenge", challenge: @challenge).order(:created_at).reverse_order
flash.now[:success] = "Flag accepted!" if @solved
flash.now[:success] = "Flag accepted!" if @solved || @admin_flag
@title = @challenge.name
@subtitle = pluralize(@challenge.point_value, "point")
@submitted_flags = to_timeline SubmittedFlag.where("challenge_id=?",params[:id]).group_by {|sf| sf.updated_at.change(:sec=>0)}
Expand All @@ -31,11 +37,27 @@ def submit_flag
end

# handle flag
if @challenge.flag == flag
flag_found = @challenge.flags.select{|flag_obj| flag_obj.flag.downcase.eql? flag.downcase }.first

if flag_found

# Make API call if one is specified. This throws away any failed requests since
# we really don't care that much if the actions really happen. The player properly
# being credited their points is more important.
@api_url = flag_found.api_request
Thread.new do
begin
Net::HTTP.get(URI.parse(@api_url)) if @api_url
rescue Exception
end
end

if is_player
SolvedChallenge.create(player: current_user, challenge: @challenge)
SolvedChallenge.create(player: current_user, challenge: @challenge, flag: flag_found)
redirect_to @challenge, flash: { success: "Flag accepted!" }
else
redirect_to challenge_url(@challenge, flag: flag_found), flash: { success: "Flag accepted!" }
end
redirect_to @challenge, flash: { success: "Flag accepted!" }
else
redirect_to @challenge, flash: { error: Rails.configuration.wrong_flag_messages.sample }
end
Expand Down
4 changes: 4 additions & 0 deletions app/helpers/challenges_helper.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
module ChallengesHelper
def embed(youtube_url)
youtube_id = youtube_url.split("v=").last
content_tag(:iframe, nil, src: "//www.youtube.com/embed/#{youtube_id}", :frameborder=>0)
end
end
8 changes: 7 additions & 1 deletion app/models/category.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ class Category < ActiveRecord::Base

validates :name, :game_id, presence: true

# The next challenge can have a greater than or equal to point value of the current one
# and has a name that comes after the current one. The order in which elements are returned
# to self.challenges in is set in the challenges model.
def next_challenge(challenge)
self.challenges.order("point_value ASC").where("point_value > ?", challenge.point_value).first
# Order of challenges is handled by default scope in challenge.rb
challenges = self.challenges
index = challenges.find_index(challenge)
challenges.at(index + 1)
end

end
35 changes: 30 additions & 5 deletions app/models/challenge.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
class Challenge < ActiveRecord::Base

belongs_to :category

has_many :flags, :dependent => :destroy

validates :name, :point_value, :flag, :category_id, :state, presence: true
validates :state, inclusion: %w( open closed )
validates :name, :point_value, :flags, :category_id, :state, presence: true
validates :state, inclusion: %w( open closed force_closed )

# Handles the ordering of all returned challenge objects.
default_scope -> { order(:point_value, :name) }

attr_accessor :submitted_flag

def state_enum
['open', 'closed']
['open', 'closed', 'force_closed']
end

def open?
Expand All @@ -18,9 +23,29 @@ def open?
def is_solved?
(SolvedChallenge.where("challenge_id = :challenge", challenge: self).count > 0)
end


# Returns whether or not challenge is available to be opened.
def available?
self.state.eql? "closed"
end

def get_current_solved_challenge(user)
solved_challenge = SolvedChallenge.where("challenge_id = :challenge and user_id = :user", challenge: self, user: user)
solved_challenge.first unless solved_challenge.nil?
end

def is_solved_by_user?(user)
(SolvedChallenge.where("challenge_id = :challenge and user_id = :user", challenge: self, user: user).count > 0)
!get_current_solved_challenge(user).nil?
end

def get_video_url_for_flag(user)
current_challenge = get_current_solved_challenge(user)
current_challenge.flag.video_url unless current_challenge.nil? || current_challenge.flag.nil?
end

def get_api_request_for_flag(user)
current_challenge = get_current_solved_challenge(user)
current_challenge.flag.api_request unless current_challenge.nil? || current_challenge.flag.nil?
end

end
7 changes: 7 additions & 0 deletions app/models/flag.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Flag < ActiveRecord::Base
belongs_to :challenge

has_many :solved_challenges

validates :flag, presence: true
end
12 changes: 10 additions & 2 deletions app/models/game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,16 @@ def open?
end

def ordered_players
self.players.sort do |a, b|
eligible_players = filter_and_sort_players(eligible: true)
ineligible_players = filter_and_sort_players(eligible: false)
eligible_players.push(*ineligible_players)
end

private

# Sorts the provided list of players.
def filter_and_sort_players(filters)
self.players.where(filters).sort do |a, b|

#
# get scores
Expand Down Expand Up @@ -84,7 +93,6 @@ def ordered_players
else
b_score <=> a_score
end

end
end

Expand Down
6 changes: 4 additions & 2 deletions app/models/solved_challenge.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
class SolvedChallenge < FeedItem

validates :challenge_id, presence: true
validates :challenge_id, :flag, presence: true
validate :user_has_not_solved_challenge, :challenge_is_open, :game_is_open

after_save :award_achievement, :open_next_challenge

belongs_to :flag

def description
%[Solved challenge "#{self.challenge.category.name} #{self.challenge.point_value}"]
Expand Down Expand Up @@ -45,7 +47,7 @@ def open_next_challenge
challenge = self.challenge
category = challenge.category
challenge = category.next_challenge(challenge)
if challenge
if challenge && challenge.available?
challenge.update_attribute(:state, "open")
end
end
Expand Down
16 changes: 16 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,22 @@ class User < ActiveRecord::Base
def admin?
self.is_a?(Admin)
end

def display_name
if self.eligible
return read_attribute(:display_name)
else
return read_attribute(:display_name) + " (ineligible)"
end
end

def set_password; nil; end

def set_password=(value)
return nil if value.blank?
self.password = value
self.password_confirmation = value
end


private
Expand Down
2 changes: 1 addition & 1 deletion app/views/challenges/index.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
- max.times do |row|
%tr
- @categories.count.times do |column|
- challenges = @categories[column].challenges.order("point_value ASC")
- challenges = @categories[column].challenges
- if row < challenges.count
- challenge = challenges[row]
- is_solved = false
Expand Down
4 changes: 4 additions & 0 deletions app/views/challenges/show.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
No description
- else
= raw(BlueCloth.new(@challenge.description).to_html)
- if @solved_video_url
.embed-container
= embed(@solved_video_url)

%h2 Solved By
= render partial: "events/timeline", locals: {id: "timeline", data: @submitted_flags}

Expand Down
12 changes: 10 additions & 2 deletions config/initializers/rails_admin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
config.label_methods << :email

config.authorize_with do
redirect_to root_path unless current_user.admin?
redirect_to main_app.root_path unless current_user.try(:admin?)
end

config.model User do
Expand Down Expand Up @@ -39,14 +39,16 @@
end

config.model Player do
configure :set_password
edit do
field :email do
label "Login"
end
field :city do
label "Location"
end
fields :password, :password_confirmation, :display_name, :tags, :game, :city, :affiliation
exclude_fields :password, :password_confirmation
fields :display_name, :tags, :game, :city, :affiliation, :eligible, :set_password
end
list do
fields :id, :display_name, :current_sign_in_at, :locked_at, :game
Expand Down Expand Up @@ -86,6 +88,12 @@
fields :name, :point_value, :state, :category
end
end

config.model Flag do
modal do
fields :flag, :api_request, :video_url
end
end

config.model SolvedChallenge do
list do
Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20150812172020_remove_flag_from_challenge.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class RemoveFlagFromChallenge < ActiveRecord::Migration
def change
remove_column :challenges, :flag, :string
end
end
12 changes: 12 additions & 0 deletions db/migrate/20150812172928_create_flags.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class CreateFlags < ActiveRecord::Migration
def change
create_table :flags do |t|
t.references :challenge, index: true, foreign_key: true
t.string :flag
t.string :api_request
t.string :video_url

t.timestamps null: false
end
end
end
5 changes: 5 additions & 0 deletions db/migrate/20150812232430_add_flag_to_feed_items.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddFlagToFeedItems < ActiveRecord::Migration
def change
add_reference :feed_items, :flag, index: true, foreign_key: true
end
end
5 changes: 5 additions & 0 deletions db/migrate/20150818183333_add_eligibility_to_users.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddEligibilityToUsers < ActiveRecord::Migration
def change
add_column :users, :eligible, :boolean, :default => true
end
end
Loading

0 comments on commit c6a96ce

Please sign in to comment.