Authorization
The big picture:
I want to authorize as soon as possible and redirect if he/she is not authorized to do current action.
Then the authorization class will need to know the user, the resource and the action. The user id is in session and the resource and the action are in the request.
I will define an authorization class for each resource, that receives current_user
and request
and responds with something that can be evaluated as true
if the user is authorized to do this action, otherwise as false
. Each authorization class will hold the access policy to its resource.
Defining authorizations:
If only logged in users can access to tasks. Then authorization for tasks could be:
apps/main/lib/todo/main/authorizations/tasks.rb
# frozen_string_literal: true
module Todo
module Main
module Authorizations
class Tasks
def call(user, _request)
user
end
end
end
end
end
It will be needed a mechanism that calls the right Todo::Main::Authorizations::<Class>
depending on the resource required. Then in apps/main/lib/todo/main/authorization.rb
I put:
# frozen_string_literal: true
module Todo
module Main
class Authorization
def call(user, request)
name = Inflecto.camelize(
request.path.split('/').reject(&:empty?).first
)
Object.const_get("Todo::Main::Authorizations::#{name}").new.(user, request)
rescue NameError
false
end
end
end
end
Putting all together
Update apps/main/system/todo/main/web.rb
with:
- Define some public routes in application's config
- Add an instance method to access config
- Add an
authorize
method - Redirect to root if authorize returns false
The call to the authorize
method should be before r.multi_route
call, the best place should be at the beginning of the route
block.
module Todo
module Main
class Web < Dry::Web::Roda::Application
...
setting :public_routes
configure do |config|
...
config.public_routes = %w[/ /login /logout /signup]
end
...
route do |r|
unless authorize
flash[:alert] = 'Unauthorized!'
r.redirect '/'
end
r.multi_route
...
end
...
private
def config
self.class.config
end
def authorize
return true if config.public_routes.include? request.path
self.class['authorization'].(current_user, request)
end
end
end
end