Reputation: 1044
I use canary lib in my phoenix app.
I wrote in config.exs:
config :canary, repo: MyApp.Repo,
unauthorized_handler: {MyApp.Helpers, :handle_unauthorized},
not_found_handler: {MyApp.Helpers, :handle_not_found}
and created helper:
defmodule MyApp.Helpers do
use Phoenix.Controller
require Logger
import Plug.Conn
def handle_unauthorized(conn) do
conn
|> put_status(401)
|> put_view(MyApp.ErrorView)
|> render("401.json")
|> halt
end
def handle_not_found(conn) do
conn
|> put_status(404)
|> put_view(MyApp.ErrorView)
|> render("404.json")
|> halt
end
end
my abilities.ex:
defimpl Canada.Can, for: MyApp.User do
alias MyApp.Operation
alias MyApp.User
def can?(user, action, operation = %Operation{}) when action in [:show, :update, :delete] do
IO.puts "check ability for operation"
if operation.user_id == user.id do
IO.puts "success"
true
else
false
end
end
def can?(subject, action, resource) do
raise """
Unimplemented authorization check for User! To fix see below...
Please implement `can?` for User in #{__ENV__.file}.
The function should match:
subject: #{inspect subject}
action: #{action}
resource: #{inspect resource}
"""
end
end
And unauthorized_handler works fine. But not_found_handler does not work.
[error] #PID<0.709.0> running MyApp.Endpoint terminated
Server: localhost:4000 (http)
Request: GET /api/v1/operations/11
** (exit) an exception was raised:
** (RuntimeError) Unimplemented authorization check for User! To fix see below...
Please implement `can?` for User in /home/mars/phoenix_projects/my_app/lib/abilities.ex.
if I add to my abilities.ex this function:
def can?(user, action, nil) do
IO.puts "check ability for nil"
false
end
If the resource is not found, I will be called handle_unauthorized
If I leave only
def can?(user, action, operation = %Operation{}) when action in [:show, :update, :delete] do
Then I will have an error
(FunctionClauseError) no function clause matching in Canada.Can.MyApp.User.can?/3
I do not understand how I need to write a function for this case
Upd: full error message:
[error] #PID<0.427.0> running MyApp.Endpoint terminated
Server: localhost:4000 (http)
Request: GET /api/v1/operations/11
** (exit) an exception was raised:
** (RuntimeError) Unimplemented authorization check for User! To fix see below...
Please implement `can?` for User in /home/mars/phoenix_projects/my_app/lib/abilities.ex.
The function should match:
subject: %MyApp.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, operations: [%MyApp.Operation{__meta__: #Ecto.Schema.Metadata<:loaded, "operations">, balance: 1.3e3, categories: #Ecto.Association.NotLoaded<association :categories is not loaded>, id: 1, inserted_at: ~N[2017-04-15 15:00:38.727984], name: "new name", updated_at: ~N[2017-04-15 15:45:08.860944], users: #Ecto.Association.NotLoaded<association :users is not loaded>}, %MyApp.Operation{__meta__: #Ecto.Schema.Metadata<:loaded, "operations">, balance: 0.0, categories: #Ecto.Association.NotLoaded<association :categories is not loaded>, id: 2, inserted_at: ~N[2017-04-15 15:00:59.396742], name: "test1", updated_at: ~N[2017-04-15 15:00:59.396747], users: #Ecto.Association.NotLoaded<association :users is not loaded>}, %MyApp.Operation{__meta__: #Ecto.Schema.Metadata<:loaded, "operations">, balance: 0.0, categories: #Ecto.Association.NotLoaded<association :categories is not loaded>, id: 3, inserted_at: ~N[2017-04-15 15:01:56.845078], name: "test0", updated_at: ~N[2017-04-15 15:01:56.845085], users: #Ecto.Association.NotLoaded<association :users is not loaded>}, %MyApp.Operation{__meta__: #Ecto.Schema.Metadata<:loaded, "operations">, balance: 0.0, categories: #Ecto.Association.NotLoaded<association :categories is not loaded>, id: 4, inserted_at: ~N[2017-04-15 15:04:25.647952], name: "testtt", updated_at: ~N[2017-04-15 15:04:25.647958], users: #Ecto.Association.NotLoaded<association :users is not loaded>}], confirmation_token: nil, confirmed: false, id: 1, inserted_at: ~N[2017-04-15 14:58:48.263515], login: "test user0", password: nil, password_confirmation: nil, password_hash: "$2b$12$8yt6WNBt5hyxZE1MqiZtX.YRuReOuy4zom1imQvK2Q.Dw6pc8iNSG", reset_password_token: nil, sessions: #Ecto.Association.NotLoaded<association :sessions is not loaded>, updated_at: ~N[2017-04-15 14:58:48.268564]}
action: show
resource: nil
(my_app) lib/abilities.ex:43: Canada.Can.MyApp.User.can?/3
(canary) lib/canary/plugs.ex:214: Canary.Plugs.do_authorize_resource/2
(canary) lib/canary/plugs.ex:187: Canary.Plugs.authorize_resource/2
(canary) lib/canary/plugs.ex:274: Canary.Plugs.do_load_and_authorize_resource/2
(my_app) web/controllers/v1/operation_controller.ex:1: MyApp.V1.OperationController.phoenix_controller_pipeline/2
(my_app) lib/my_app/endpoint.ex:1: MyApp.Endpoint.instrument/4
(my_app) lib/phoenix/router.ex:261: MyApp.Router.dispatch/2
(my_app) web/router.ex:1: MyApp.Router.do_call/2
(my_app) lib/my_app/endpoint.ex:1: MyApp.Endpoint.phoenix_pipeline/1
(my_app) lib/plug/debugger.ex:123: MyApp.Endpoint."call (overridable 3)"/2
(my_app) lib/my_app/endpoint.ex:1: MyApp.Endpoint.call/2
(plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
(cowboy) /home/mars/phoenix_projects/my_app/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4
Upvotes: 0
Views: 161
Reputation: 4517
If you return true for the can?(_, _, nil)
function clause, this will allow the authorization to pass and the handle_not_found plug to set the conn
properly.
Upvotes: 1