e3f006b57692f3a40314c35b56427d2edc50751d
[arvados.git] / app / controllers / application_controller.rb
1 class ApplicationController < ActionController::Base
2   protect_from_forgery
3   before_filter :find_object_by_uuid, :except => [:index, :render_error, :render_not_found]
4   around_filter :thread_with_api_token, :except => [:render_error, :render_not_found]
5
6   unless Rails.application.config.consider_all_requests_local
7     rescue_from Exception,
8     :with => :render_error
9     rescue_from ActiveRecord::RecordNotFound,
10     :with => :render_not_found
11     rescue_from ActionController::RoutingError,
12     :with => :render_not_found
13     rescue_from ActionController::UnknownController,
14     :with => :render_not_found
15     rescue_from ActionController::UnknownAction,
16     :with => :render_not_found
17   end
18
19   def error(opts)
20     respond_to do |f|
21       f.html { render opts.merge(controller: 'application', action: 'error') }
22       f.json { render opts.merge(json: {success: false, errors: @errors}) }
23     end
24   end
25
26   def render_error(e)
27     logger.error e.inspect
28     logger.error e.backtrace.collect { |x| x + "\n" }.join('') if e.backtrace
29     if @object and @object.errors and @object.errors.full_messages
30       @errors = @object.errors.full_messages
31     else
32       @errors = [e.inspect]
33     end
34     self.error status: 422
35   end
36
37   def render_not_found(e=ActionController::RoutingError.new("Path not found"))
38     logger.error e.inspect
39     @errors = ["Path not found"]
40     self.error status: 404
41   end
42
43
44   def index
45     @objects ||= model_class.all
46     respond_to do |f|
47       f.json { render json: @objects }
48     end
49   end
50
51   def show
52     if !@object
53       render_not_found("object not found")
54     end
55     respond_to do |f|
56       f.json { render json: @object }
57     end
58   end
59
60   protected
61     
62   def model_class
63     controller_name.classify.constantize
64   end
65
66   def find_object_by_uuid
67     if params[:id] and params[:id].match /\D/
68       params[:uuid] = params.delete :id
69     end
70     @object = model_class.where('uuid=?', params[:uuid]).first
71   end
72
73   def thread_with_api_token
74     begin
75       try_redirect_to_login = true
76       if params[:api_token]
77         try_redirect_to_login = false
78         Thread.current[:orvos_api_token] = params[:api_token]
79         # Before copying the token into session[], do a simple API
80         # call to verify its authenticity.
81         if verify_api_token
82           session[:orvos_api_token] = params[:api_token]
83           if !request.format.json? and request.method == 'GET'
84             # Repeat this request with api_token in the (new) session
85             # cookie instead of the query string.  This prevents API
86             # tokens from appearing in (and being inadvisedly copied
87             # and pasted from) browser Location bars.
88             redirect_to request.fullpath.sub(%r{([&\?]api_token=)[^&\?]*}, '')
89           else
90             yield
91           end
92         else
93           @errors = ['Could not verify API token.']
94           self.error status: 401
95         end
96       elsif session[:orvos_api_token]
97         # In this case, the token must have already verified at some
98         # point, but it might have been revoked since.  We'll try
99         # using it, and catch the exception if it doesn't work.
100         try_redirect_to_login = false
101         Thread.current[:orvos_api_token] = session[:orvos_api_token]
102         begin
103           yield
104         rescue OrvosApiClient::NotLoggedInException
105           try_redirect_to_login = true
106         end
107       end
108       if try_redirect_to_login
109         respond_to do |f|
110           f.html {
111             redirect_to $orvos_api_client.orvos_login_url(return_to: request.url)
112           }
113           f.json {
114             @errors = ['No API token supplied -- can\'t really do anything.']
115             self.error status: 422
116           }
117         end
118       end
119     ensure
120       # Remove token in case this Thread is used for anything else.
121       Thread.current[:orvos_api_token] = nil
122     end
123   end
124
125   def verify_api_token
126     Metadatum.where(uuid: 'the-philosophers-stone').size rescue false
127   end
128 end