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).



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

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:

“‘ruby 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.



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

def scope
  @scope
end

Instance Method Details

#agentObject



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

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

#authenticationObject



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

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

#hostObject



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

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

#ipObject



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

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

#locationObject



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

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

#must_be_delete!Object



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

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

#must_be_get!Object



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

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

#must_be_html!Object



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

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

#must_be_json!Object



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

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

#must_be_logged_in!Object



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

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



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

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

#must_be_post!Object



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

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 `example.com` (no port) when the port is the scheme default. The browser sends `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.



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

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

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

#paramsObject



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

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



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

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

#session(namespace:) ⇒ Object



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

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

#storageObject



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

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

#sudoObject



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

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