Merge branch 'master' into 5383-api-db-current-time
[arvados.git] / services / api / app / middlewares / arvados_api_token.rb
1 # Perform api_token checking very early in the request process.  We want to do
2 # this in the Rack stack instead of in ApplicationController because
3 # websockets needs access to authentication but doesn't use any of the rails
4 # active dispatch infrastructure.
5 require 'db_current_time'
6
7 class ArvadosApiToken
8   include DbCurrentTime
9
10   # Create a new ArvadosApiToken handler
11   # +app+  The next layer of the Rack stack.
12   def initialize(app = nil, options = nil)
13     @app = app if app.respond_to?(:call)
14   end
15
16   def call env
17     # First, clean up just in case we have a multithreaded server and thread
18     # local variables are still set from a prior request.  Also useful for
19     # tests that call this code to set up the environment.
20     Thread.current[:api_client_ip_address] = nil
21     Thread.current[:api_client_authorization] = nil
22     Thread.current[:api_client_uuid] = nil
23     Thread.current[:api_client] = nil
24     Thread.current[:user] = nil
25
26     request = Rack::Request.new(env)
27     params = request.params
28     remote_ip = env["action_dispatch.remote_ip"]
29
30     Thread.current[:request_starttime] = db_current_time
31     user = nil
32     api_client = nil
33     api_client_auth = nil
34     supplied_token =
35       params["api_token"] ||
36       params["oauth_token"] ||
37       env["HTTP_AUTHORIZATION"].andand.match(/OAuth2 ([a-z0-9]+)/).andand[1]
38     if supplied_token
39       api_client_auth = ApiClientAuthorization.
40         includes(:api_client, :user).
41         where('api_token=? and (expires_at is null or expires_at > CURRENT_TIMESTAMP)', supplied_token).
42         first
43       if api_client_auth.andand.user
44         user = api_client_auth.user
45         api_client = api_client_auth.api_client
46       else
47         # Token seems valid, but points to a non-existent (deleted?) user.
48         api_client_auth = nil
49       end
50     end
51     Thread.current[:api_client_ip_address] = remote_ip
52     Thread.current[:api_client_authorization] = api_client_auth
53     Thread.current[:api_client_uuid] = api_client.andand.uuid
54     Thread.current[:api_client] = api_client
55     Thread.current[:user] = user
56     if api_client_auth
57       api_client_auth.last_used_at = db_current_time
58       api_client_auth.last_used_by_ip_address = remote_ip.to_s
59       api_client_auth.save validate: false
60     end
61
62     @app.call env if @app
63   end
64 end