class MtaJson::Wrapper

wraps calls to .mta-urls in a MTA-specific format. The main difference is:

MTA technically allows multiple parameters, but that doesn’t really suit rails’ named parameters (param).

Constants

ALLOWED_METHODS

Allowed Methods to be set by MTA

ALLOWED_METHODS_PRIVATE
BODY
CONTENT_LENGTH

HTTP-Response-Headers

CONTENT_TYPE

env

CSRF_TOKEN
FORM_HASH
FORM_INPUT
IP
METHOD
PATH_INFO
POST
SESSION

Public Class Methods

new(app) click to toggle source
# File lib/mta_json/wrapper.rb, line 29
def initialize app
  @app = app
end

Public Instance Methods

call(env) click to toggle source

callRemote on the MTA-side may have [0, 1 or 2] parameter(s):

  • if no parameters, params is empty within rails

  • first parameter - as a table - fills the params hash which is accessible in controllers (etc) i.e.

    callRemote("http://localhost/auth.mta", function, { name = 'a', password = 'b' })

    makes the following parameters available in the called controller

    params[:name] = 'a'
    params[:password] = 'b'
    
  • second parameter - as a table - options on how the request is handled. See update_options for details, for example { ‘method’ = ‘GET’ } allows GET-Requests, while usually callRemote does only POST-Requests.

This is unlike the PHP-SDK, which may have multiple parameters and allows access via numbers

$input = mta::getInput();
$name = $input[0];
$password = $input[1];

Rails has named parameters, so there’s no explicit support for them numbers.

callRemote for MTA should always call files ending with .mta, for example:

  • /auth => /auth.mta

This will call the related JSON-Methods, i.e.

respond_to do |format|
  format.html { ... } # certainly not this
  format.json { render :json => @object } # this gets called!
end

There is no support for elements and/or resources (yet?).

# File lib/mta_json/wrapper.rb, line 60
def call env
  path = env[PATH_INFO]
  if path.end_with?('.mta') and (body = env[BODY].read).length != 0 and env[METHOD] == POST
    # replace ".mta" with ".json"
    env[PATH_INFO] = path[0..-5] + '.json'

    json = JSON.parse body
    raise Exception, "Number of JSON elements > 2: actual #{json.size}" if json.size > 2

    # optional options!
    update_options env, json[1].with_indifferent_access if json.size >= 2

    verify_request_method env

    # any parameters given? otherwise we don't really care for any params
    update_params env, json[0] if json.size >= 1 and json[0] != 0 # 0 is nil in terms of callRemote. JSON has 'null' though!

    # add some CSRF info... maybe.
    add_csrf_info env

    # call the other middlewares
    status, headers, response = @app.call(env)

    # alter only successful status codes to be more mta-ish
    if (200..299).include?(status)
      # mta /can/ handle multiple parameters as response.
      # We do not want and/or need this, but merely wrap the response body in square brackets as to return a single element
      # If render :json => @obj is used in the controller, this should make exactly @obj available in MTA.
      response = to_response(response, headers)

      # overwrite the length if already set
      if headers['Content-Length']
        headers['Content-Length'] = response.length
      end
    end

    return [status, headers, response]
  else
    # normal call, nevermind that whole mta business
    @app.call(env)
  end
end

Private Instance Methods

add_csrf_info(env) click to toggle source

adds csrf info to non-GET requests of whitelisted IPs

# File lib/mta_json/wrapper.rb, line 129
def add_csrf_info env
  env[CSRF_TOKEN] = env[SESSION][:_csrf_token] = SecureRandom.base64(32).to_s if env[METHOD] != 'GET' and whitelisted?(env)
end
to_response(response, headers) click to toggle source

returns the response body: old response body + headers as a JSON array

# File lib/mta_json/wrapper.rb, line 139
def to_response response, headers
  body = ""
  response.each do |s|
    body << s.to_s
  end
  ["[#{body},#{headers.to_json.to_s}]"]
end
update_options(env, options) click to toggle source

updates the options

# File lib/mta_json/wrapper.rb, line 120
def update_options env, options
  if options[:method] and (ALLOWED_METHODS | ALLOWED_METHODS_PRIVATE).include?(options[:method])
    # (possibly) TODO - pass parameters for GET instead of POST in update_params then?
    # see https://github.com/rack/rack/blob/master/lib/rack/request.rb -> def GET
    env[METHOD] = options[:method]
  end
end
update_params(env, json) click to toggle source

update all of the parameter-related values in the current request’s environment

# File lib/mta_json/wrapper.rb, line 105
def update_params env, json
  env[FORM_HASH] = json
  env[BODY] = env[FORM_INPUT] = StringIO.new(Rack::Utils.build_query(json))
end
verify_request_method(env) click to toggle source

make sure the request came from a whitelisted ip, or uses a publically accessible request method

# File lib/mta_json/wrapper.rb, line 111
def verify_request_method env
  allowed = ALLOWED_METHODS
  allowed |= ALLOWED_METHODS_PRIVATE if whitelisted?(env)
  if !allowed.include?(env[METHOD])
    raise "Request method #{env[METHOD]} not allowed"
  end
end
whitelisted?(env) click to toggle source

TODO might need to revise this behing proxies

# File lib/mta_json/wrapper.rb, line 134
def whitelisted? env
  Railtie.config.mta_json.whitelist.include?(env[IP])
end