Skip to content

Commit

Permalink
Secures the API for the grades using access tokens. User can reset th…
Browse files Browse the repository at this point in the history
…eir token at any time.
  • Loading branch information
antonrd committed Nov 22, 2013
1 parent d213b30 commit 0586ad8
Show file tree
Hide file tree
Showing 22 changed files with 137 additions and 10 deletions.
4 changes: 4 additions & 0 deletions Gemfile
Expand Up @@ -9,6 +9,10 @@ gem 'mysql2', '~> 0.3.11'
gem 'json', '1.8.0'
gem 'devise', '~> 3.0'

group :development, :test do
gem 'debugger'
end

# Gems used only for assets and not required
# in production environments by default.
group :assets do
Expand Down
8 changes: 8 additions & 0 deletions Gemfile.lock
Expand Up @@ -39,6 +39,13 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.6.3)
columnize (0.3.6)
debugger (1.6.2)
columnize (>= 0.3.1)
debugger-linecache (~> 1.2.0)
debugger-ruby_core_source (~> 1.2.3)
debugger-linecache (1.2.0)
debugger-ruby_core_source (1.2.3)
devise (3.1.1)
bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.1)
Expand Down Expand Up @@ -116,6 +123,7 @@ PLATFORMS

DEPENDENCIES
coffee-rails (~> 3.2.1)
debugger
devise (~> 3.0)
jquery-rails
json (= 1.8.0)
Expand Down
3 changes: 3 additions & 0 deletions app/assets/javascripts/users.js.coffee
@@ -0,0 +1,3 @@
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
3 changes: 3 additions & 0 deletions app/assets/stylesheets/users.css.scss
@@ -0,0 +1,3 @@
// Place all the styles related to the users controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
8 changes: 8 additions & 0 deletions app/controllers/application_controller.rb
@@ -1,3 +1,11 @@
class ApplicationController < ActionController::Base
protect_from_forgery

protected
def restrict_access
authenticate_or_request_with_http_token do |token, options|
api_key = ApiKey.where(access_token: token).first
api_key && api_key.user && api_key.user.email == params["email"]
end
end
end
2 changes: 2 additions & 0 deletions app/controllers/runs_controller.rb
@@ -1,4 +1,6 @@
class RunsController < ApplicationController
before_filter :restrict_access, except: [:index]

def index
@runs = current_user.runs.latest_first
end
Expand Down
3 changes: 3 additions & 0 deletions app/controllers/tasks_controller.rb
@@ -1,4 +1,7 @@
class TasksController < ApplicationController

before_filter :restrict_access, except: [:index]

def index
@tasks = current_user.tasks
end
Expand Down
7 changes: 7 additions & 0 deletions app/controllers/users_controller.rb
@@ -0,0 +1,7 @@
class UsersController < ApplicationController
def reset_token
current_user.api_key.destroy if !current_user.api_key.nil?
new_api_key = current_user.create_api_key
redirect_to edit_user_registration_path
end
end
2 changes: 2 additions & 0 deletions app/helpers/users_helper.rb
@@ -0,0 +1,2 @@
module UsersHelper
end
15 changes: 15 additions & 0 deletions app/models/api_key.rb
@@ -0,0 +1,15 @@
class ApiKey < ActiveRecord::Base
attr_accessible :access_token, :user_id

belongs_to :user

before_create :generate_access_token

private

def generate_access_token
begin
self.access_token = SecureRandom.hex
end while self.class.exists?(access_token: access_token)
end
end
1 change: 1 addition & 0 deletions app/models/user.rb
Expand Up @@ -10,4 +10,5 @@ class User < ActiveRecord::Base

has_many :tasks
has_many :runs
has_one :api_key
end
12 changes: 12 additions & 0 deletions app/views/devise/registrations/edit.html.erb
@@ -1,5 +1,17 @@
<h2>Edit <%= resource_name.to_s.humanize %></h2>

<div>
<% if current_user.api_key.nil? || current_user.api_key.access_token.nil? %>
No API token yet.
<% else %>
<b>API token:</b> <%= current_user.api_key.access_token %>
<% end %>
</div>

<div style="margin-bottom: 30px;">
<%= link_to 'Reset token', users_reset_token_path %>
</div>

<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
<%= devise_error_messages! %>

Expand Down
2 changes: 2 additions & 0 deletions app/views/users/reset_token.html.erb
@@ -0,0 +1,2 @@
<h1>Users#reset_token</h1>
<p>Find me in app/views/users/reset_token.html.erb</p>
12 changes: 8 additions & 4 deletions config/grader.yml
@@ -1,13 +1,17 @@
development:
sync: cp
sync_to: /Users/pierrelombard/tmp/sets
files_root: /Users/pierrelombard/tmp
sync_to: sets
runs_dir: runs
run_cpp: ./program < %s > output
run_java: java program
run_java: java program < %s > output
test:
sync: cp
sync_to: /Users/pierrelombard/tmp/sets
sync_to: sets
runs_dir: runs
files_root: /Users/pierrelombard/tmp
run_cpp: ./program < %s > output
run_java: java program
run_java: java program < %s > output
production:
sync: rsync -azv -e ssh --delete
sync_to: sets
2 changes: 2 additions & 0 deletions config/routes.rb
@@ -1,4 +1,6 @@
TasksGrader::Application.routes.draw do
get "users/reset_token"

devise_for :users, path_names: {sign_in: "login", sign_out: "logout"}

root :to => 'tasks#index'
Expand Down
10 changes: 10 additions & 0 deletions db/migrate/20131122100705_create_api_keys.rb
@@ -0,0 +1,10 @@
class CreateApiKeys < ActiveRecord::Migration
def change
create_table :api_keys do |t|
t.integer :user_id
t.string :access_token

t.timestamps
end
end
end
9 changes: 8 additions & 1 deletion db/schema.rb
Expand Up @@ -11,7 +11,14 @@
#
# It's strongly recommended to check this file into your version control system.

ActiveRecord::Schema.define(:version => 20131117121158) do
ActiveRecord::Schema.define(:version => 20131122100705) do

create_table "api_keys", :force => true do |t|
t.integer "user_id"
t.string "access_token"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end

create_table "runs", :force => true do |t|
t.integer "user_id"
Expand Down
15 changes: 10 additions & 5 deletions lib/grader.rb
Expand Up @@ -78,7 +78,7 @@ def run
end

def update_checker(run)
Dir.chdir(File.join(@config[:sync_to], run.task_id.to_s)) do
Dir.chdir(File.join(@config[:files_root], @config[:sync_to], run.task_id.to_s)) do
File.open("grader.log", "w") do |f|
data = ActiveSupport::JSON.decode(run.data)
if data["source_code"].empty?
Expand All @@ -100,7 +100,7 @@ def update_checker(run)
def process_task_update(run)
puts 'Update task run: %d' % run.id
from = File.join(run.user.file_path, run.data.to_s, '*')
to = File.join(@config[:sync_to], run.task_id.to_s)
to = File.join(@config[:files_root], @config[:sync_to], run.task_id.to_s)
FileUtils.mkdir_p(to)
puts "Removing %s" % File.join(to, '*')
FileUtils.rm(Dir.glob(File.join(to, '*')))
Expand All @@ -117,7 +117,10 @@ def grade(run)
puts 'Grade task run: %d' % run.id
data = ActiveSupport::JSON.decode(run.data)
puts data
Dir.chdir '/Users/pierrelombard/tmp' do
run_dir = File.join(@config[:files_root], @config[:runs_dir], run.id.to_s)
FileUtils.mkdir_p(run_dir)
FileUtils.rm(Dir.glob(File.join(run_dir, '*')))
Dir.chdir(run_dir) do
File.open("grader.log", "w") do |f|
f.sync = true
puts 'here'
Expand All @@ -131,10 +134,10 @@ def grade(run)
message: 'Compilation error',
log: File.read("grader.log"))
else
input_file_pat = File.join(@config[:sync_to], run.task_id.to_s, 'input.*.txt')
input_file_pat = File.join(@config[:files_root], @config[:sync_to], run.task_id.to_s, 'input.*.txt')
# puts input_file_pat
input_files = Dir.glob(input_file_pat)
output_file_pat = File.join(@config[:sync_to], run.task_id.to_s, 'solve.*.txt')
output_file_pat = File.join(@config[:files_root], @config[:sync_to], run.task_id.to_s, 'solve.*.txt')
output_files = Dir.glob(output_file_pat)
# puts input_files
# puts output_files
Expand All @@ -153,6 +156,8 @@ def grade(run)
end
end
end
# Remove the directory after the execution.
FileUtils.rm_rf(run_dir)
end

private
Expand Down
9 changes: 9 additions & 0 deletions test/fixtures/api_keys.yml
@@ -0,0 +1,9 @@
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html

one:
user_id: 1
access_token: MyString

two:
user_id: 1
access_token: MyString
9 changes: 9 additions & 0 deletions test/functional/users_controller_test.rb
@@ -0,0 +1,9 @@
require 'test_helper'

class UsersControllerTest < ActionController::TestCase
test "should get reset_token" do
get :reset_token
assert_response :success
end

end
7 changes: 7 additions & 0 deletions test/unit/api_key_test.rb
@@ -0,0 +1,7 @@
require 'test_helper'

class ApiKeyTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end
4 changes: 4 additions & 0 deletions test/unit/helpers/users_helper_test.rb
@@ -0,0 +1,4 @@
require 'test_helper'

class UsersHelperTest < ActionView::TestCase
end

0 comments on commit 0586ad8

Please sign in to comment.