Class: Booth::Request

Inherits:
Object
  • Object
show all
Includes:
Logging
Defined in:
lib/booth/request.rb

Overview

Convenience wrapper for Rack::Request.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(request:, scope:) ⇒ Request

Initializes a new Booth::Request instance.

Parameters:

  • :request - The request object (ActionDispatch::Request, Rack::Request, or Booth::Request).

  • scope - The controller scope identifier (Symbol).



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/booth/request.rb', line 16

def initialize(request:, scope:)
  # Sometimes we pass the request instance from one `Calls` object to another.
  # In that case, unwrap the underlying request and take that as basis.
  # if request.is_a?(::Booth::Request)
  #   return request if request.scope == scope
  #   #request = request.send(:request)
  # end

  # The underlying `request` is an `ActionDispatch::Request` or a `Rack::Request`.
  # For consistency, we always convert to the more rich interface provided by Rails.
  request = ActionDispatch::Request.new(request.env) if request.is_a?(::Rack::Request)

  @request = request
  @scope = ::Booth::Syntaxes::Scope.call(scope).normalized_scope
end

Instance Attribute Details

#scopeObject (readonly)

A Controller that logs the user in, needs to know in which scope to login to. The scope is an authoritative way for the Rails developer to specify a dedicated area.

For example:

Booth::Userland.new_login(scope: :admin, ...)
Booth::Userland.new_login(scope: :guest, ...)

The implementation could then check if a user is already logged in in that scope, while ignoring other scopes.

For convenience, we store the authoritative controller scope here, in the request wrapper.



46
47
48
# File 'lib/booth/request.rb', line 46

def scope
  @scope
end

Instance Method Details

#agentObject



69
70
71
# File 'lib/booth/request.rb', line 69

def agent
  ::Booth::Requests::Agent.call(request:)
end

#authentication(scope: self.scope) ⇒ Object



81
82
83
# File 'lib/booth/request.rb', line 81

def authentication(scope: self.scope)
  ::Booth::Requests::Authentication.new(scope:, warden: request.env['warden'])
end

#hostObject



50
51
52
# File 'lib/booth/request.rb', line 50

def host
  ::Booth::Syntaxes::Domain.call(request.host).valid_domain
end

#ipObject



73
74
75
# File 'lib/booth/request.rb', line 73

def ip
  ::Booth::Requests::Ip.call(request:)
end

#locationObject



77
78
79
# File 'lib/booth/request.rb', line 77

def location
  ::Booth::Core::Geolocation.lookup(ip)
end

#must_be_delete!Object



135
136
137
# File 'lib/booth/request.rb', line 135

def must_be_delete!
  request.delete? || raise(::Booth::Errors::MustBeDelete)
end

#must_be_get!Object



123
124
125
# File 'lib/booth/request.rb', line 123

def must_be_get!
  request.get? || raise(::Booth::Errors::MustBeGet)
end

#must_be_html!Object



115
116
117
# File 'lib/booth/request.rb', line 115

def must_be_html!
  request.format.html? || raise(::Booth::Errors::MustBeHtml)
end

#must_be_json!Object



119
120
121
# File 'lib/booth/request.rb', line 119

def must_be_json!
  request.format.json? || raise(::Booth::Errors::MustBeJson)
end

#must_be_logged_in!Object



108
109
110
111
112
113
# File 'lib/booth/request.rb', line 108

def must_be_logged_in!
  return if authentication.logged_in?

  log { "Expected someone to be logged in in scope #{scope.inspect}" }
  raise ::Booth::Errors::NotAuthenticated
end

#must_be_patch!Object



131
132
133
# File 'lib/booth/request.rb', line 131

def must_be_patch!
  request.patch? || raise(::Booth::Errors::MustBePatch)
end

#must_be_post!Object



127
128
129
# File 'lib/booth/request.rb', line 127

def must_be_post!
  request.post? || raise(::Booth::Errors::MustBePost)
end

#originObject

The webauthn gem compares the browser's origin against allowed_origins character-by-character. Hardcoding "http://#{host}:#{port}" caused WebAuthn::OriginVerificationError in production. The browser sends https://example.com (no port) when the port is the scheme default. The browser sends http://localhost:3000 (with port) when the port is not the scheme default. This method builds the origin string to match exactly what the browser sends.



59
60
61
62
63
64
65
66
67
# File 'lib/booth/request.rb', line 59

def origin
  standard_port = request.scheme == 'https' ? 443 : 80

  if port == standard_port
    "#{request.scheme}://#{host}"
  else
    "#{request.scheme}://#{host}:#{port}"
  end
end

#paramsObject



97
98
99
100
101
102
# File 'lib/booth/request.rb', line 97

def params
  # Huh, I thought the following line was needed:
  # return request.param if request.params.is_a?(::ActionController::Parameters)

  @params ||= ::ActionController::Parameters.new(request.params)
end

#return_pathObject



104
105
106
# File 'lib/booth/request.rb', line 104

def return_path
  ::Booth::Requests::ReturnPath.call(params:)
end

#session(namespace:) ⇒ Object



85
86
87
# File 'lib/booth/request.rb', line 85

def session(namespace:)
  ::Booth::Requests::Session.new(scope:, namespace:, session: request.session)
end

#storageObject



93
94
95
# File 'lib/booth/request.rb', line 93

def storage
  ::Booth::Requests::Storage.new(scope:, request: self)
end

#sudoObject



89
90
91
# File 'lib/booth/request.rb', line 89

def sudo
  ::Booth::Requests::Sudo.new(scope:, request: self)
end