19316: Change wb1 and sdk/cli to Oj.safe_load / strict_load.
[arvados.git] / apps / workbench / app / models / arvados_api_client.rb
index ca09aa719d1cddfc208d2d0e79ec74d3438aaa94..47fcc4ce51ffe1aafb288453b9d9c6a0b777bd0d 100644 (file)
@@ -1,3 +1,7 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
 require 'httpclient'
 require 'thread'
 
@@ -36,7 +40,7 @@ class ArvadosApiClient
     def initialize(request_url, api_response)
       @api_status = api_response.status_code
       @api_response_s = api_response.content
-      @api_response = Oj.load(@api_response_s, :symbol_keys => true)
+      @api_response = Oj.strict_load(@api_response_s, :symbol_keys => true)
       errors = @api_response[:errors]
       if errors.respond_to?(:join)
         errors = errors.join("\n\n")
@@ -57,7 +61,7 @@ class ArvadosApiClient
     404 => NotFoundException,
   }
 
-  @@profiling_enabled = Rails.configuration.profiling_enabled
+  @@profiling_enabled = Rails.configuration.Workbench.ProfilingEnabled
   @@discovery = nil
 
   # An API client object suitable for handling API requests on behalf
@@ -78,20 +82,26 @@ class ArvadosApiClient
     @client_mtx = Mutex.new
   end
 
-  def api(resources_kind, action, data=nil, tokens={})
+  def api(resources_kind, action, data=nil, tokens={}, include_anon_token=true)
 
     profile_checkpoint
 
     if not @api_client
       @client_mtx.synchronize do
         @api_client = HTTPClient.new
-        if Rails.configuration.arvados_insecure_https
+        @api_client.ssl_config.timeout = Rails.configuration.Workbench.APIClientConnectTimeout
+        @api_client.connect_timeout = Rails.configuration.Workbench.APIClientConnectTimeout
+        @api_client.receive_timeout = Rails.configuration.Workbench.APIClientReceiveTimeout
+        if Rails.configuration.TLS.Insecure
           @api_client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
         else
           # Use system CA certificates
-          @api_client.ssl_config.add_trust_ca('/etc/ssl/certs')
+          ["/etc/ssl/certs/ca-certificates.crt",
+           "/etc/pki/tls/certs/ca-bundle.crt"]
+            .select { |ca_path| File.readable?(ca_path) }
+            .each { |ca_path| @api_client.ssl_config.add_trust_ca(ca_path) }
         end
-        if Rails.configuration.api_response_compression
+        if Rails.configuration.Workbench.APIResponseCompression
           @api_client.transparent_gzip_decompression = true
         end
       end
@@ -103,14 +113,13 @@ class ArvadosApiClient
     # Clean up /arvados/v1/../../discovery/v1 to /discovery/v1
     url.sub! '/arvados/v1/../../', '/'
 
+    anon_tokens = [Rails.configuration.Users.AnonymousUserToken].select { |x| !x.empty? && include_anon_token }
+
     query = {
-      'api_token' => (tokens[:arvados_api_token] ||
-                      Thread.current[:arvados_api_token] ||
-                      ''),
       'reader_tokens' => ((tokens[:reader_tokens] ||
                            Thread.current[:reader_tokens] ||
                            []) +
-                          [Rails.configuration.anonymous_user_token]).to_json,
+                          anon_tokens).to_json,
     }
     if !data.nil?
       data.each do |k,v|
@@ -121,7 +130,7 @@ class ArvadosApiClient
         elsif v == false
           query[k] = 0
         else
-          query[k] = JSON.dump(v)
+          query[k] = Oj.dump(v, mode: :compat)
         end
       end
     else
@@ -132,12 +141,19 @@ class ArvadosApiClient
       query["_profile"] = "true"
     end
 
-    header = {"Accept" => "application/json"}
+    headers = {
+      "Accept" => "application/json",
+      "Authorization" => "OAuth2 " +
+                         (tokens[:arvados_api_token] ||
+                          Thread.current[:arvados_api_token] ||
+                          ''),
+      "X-Request-Id" => Thread.current[:request_id] || '',
+    }
 
     profile_checkpoint { "Prepare request #{query["_method"] or "POST"} #{url} #{query[:uuid]} #{query.inspect[0,256]}" }
     msg = @client_mtx.synchronize do
       begin
-        @api_client.post(url, query, header: header)
+        @api_client.post(url, query, headers)
       rescue => exception
         raise NoApiResponseException.new(url, exception)
       end
@@ -151,7 +167,7 @@ class ArvadosApiClient
     end
 
     begin
-      resp = Oj.load(msg.content, :symbol_keys => true)
+      resp = Oj.strict_load(msg.content, :symbol_keys => true)
     rescue Oj::ParseError
       resp = nil
     end
@@ -219,17 +235,13 @@ class ArvadosApiClient
   end
 
   def arvados_login_url(params={})
-    if Rails.configuration.respond_to? :arvados_login_base
-      uri = Rails.configuration.arvados_login_base
-    else
-      uri = self.arvados_v1_base.sub(%r{/arvados/v\d+.*}, '/login')
-    end
-    if params.size > 0
-      uri += '?' << params.collect { |k,v|
-        CGI.escape(k.to_s) + '=' + CGI.escape(v.to_s)
-      }.join('&')
+    uri = URI.parse(Rails.configuration.Services.Controller.ExternalURL.to_s)
+    if Rails.configuration.testing_override_login_url
+      uri = URI(Rails.configuration.testing_override_login_url)
     end
-    uri
+    uri.path = "/login"
+    uri.query = URI.encode_www_form(params)
+    uri.to_s
   end
 
   def arvados_logout_url(params={})
@@ -237,7 +249,11 @@ class ArvadosApiClient
   end
 
   def arvados_v1_base
-    Rails.configuration.arvados_v1_base
+    # workaround Ruby 2.3 bug, can't duplicate URI objects
+    # https://github.com/httprb/http/issues/388
+    u = URI.parse(Rails.configuration.Services.Controller.ExternalURL.to_s)
+    u.path = "/arvados/v1"
+    u.to_s
   end
 
   def discovery