adrian
Hi,
I’ve written a plug that checks if the user has privileges. If it doesn’t it returns:
defp allow_superusers(_, conn) do
conn
|> put_status(403)
end
Is there any way phoenix can catch those status and render a 403.html or a
text? Is a good idea to use the render view inside the plug?
I’ve also tried:
defp allow_superusers(_, conn) do
conn
|> send_resp(403, "Not allowed")
|> halt
end
And my final choice was using custom errors so I created a custom exception and the function was like:
defp allow_superusers(_, _conn) do
raise App.UnauthorizedError
end
where the exception is deffined like:
defmodule App.UnauthorizedError do
defexception message: "Unauthorized", accepts: [], plug_status: 403
end
But I have some concerns. Reading the docs and some books (specially Programing Elixir 1.2), It’s said that in elixir exceptions should be used for things that should never happen, so just for very exceptional cases.
As an example, it says that mix has no exception handlers in the code, and elixir has a total of five. I’ve read some code for Ecto and Phoenix and both define a few exceptions:
- phoenix/lib/phoenix/exceptions.ex at main · phoenixframework/phoenix · GitHub
- ecto/lib/ecto/exceptions.ex at master · elixir-ecto/ecto · GitHub
I’m totally new to elixir and phoenix, but I think 403 statuses don’t fall under the “exceptional event” that takes a process down like if the database goes down.
Maybe some experience developers could give us some advise here.
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #hex
- #security











Showing Posts 1 to 9- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
OvermindDL1
I do my permission testing, which if that fails it throws match error of a special tagged tuple, which I catch in my generic error catcher here:
Where
do_unauthorized/1is:Where
unauthorized/1is:It just gets the location of where they were, the query, stories it, and redirects to the auth controller to have them log in with a better account (which immediately clears the stored path from the conn but stores it in another place internally and will redirect to it if they successfully log in with another account, among some other stuff). This
unauthorized/1function could easily just set a 403 status and render a template though.adrian
Is it a good practice to call ControllerCallbacks.unauthorized(conn) from the plug? Or maybe unauthorized/1 could be inside the plug?
This is the plug:
OvermindDL1
I like to have everything that mutates a connection in a controller somewhere, or the helpers for my shared bits, absolutely unnecessary though, just an organizational thing for me.
EDIT: I would not really raise the error there, I have a wrapper around all my leaf-node controllers to catch my custom errors.
adrian
So, If I understand correctly, you propose to alter the connection in a controller module calling the function in the plug like:
what do you mean with having a wrapper around all the leaf-node controllers? I want to avoid using custom errors, so I really like the idea you proposed of using a function like this:
OvermindDL1
My controller commands are like this (this is before a refactor in rearranging things, but still):
The
happy_path!(else: handle_error(conn)) dohandles the errors. The lines@perm true = conn |> can?(show(%Perms.CheckInOut{section: true}))and@perm true = conn |> can?(show(%Perms.CheckInOut{section: section.slug}))test permissions and return true if successful, so if that fails then it fails the match totrue, and thus gets tagged with a:permtuple via the@permtag, which gets tossed into thehandle_error(conn)call, which enters the path you saw before. I have very fine-grained permissions that are built up based on data that gets accessed (with a lot of early tests, like here it tests if they have access to ‘anything’ in the section, if true then it gets the section information and tests if they have access to this specific section, if true then it continues). This is why a Plug style permission tests would not even remotely work for me, they are far too coarse-grained, at least without multiple DB lookups, which…why, or stuffing theconnassignwith lots of stuff?adrian
Very useful, thank you!!
christopheradams
As an aside: for the benefit of fellow programmers and your future self, you should consider renaming your errors and functions from “Unauthorized” to “Forbidden”, as the former suggests (at least to me) a 401 error rather than 403.
bobbypriambodo
Let me join the fun
I was the one who suggested @adrian to use custom errors on the #phoenix-talk google group (as a fellow beginner). I personally used the method when implementing parameter validation for my routes (using GitHub - vic/params: Easy parameters validation/casting with Ecto.Schema, akin to Rails' strong parameters. · GitHub, raising
InvalidParamsErroronnot ch.valid?). My justification is that I wanted to treat it as422, and the Phoenix docs seem to suggest using custom errors for that (as they have been doing withEcto.NoResultsErrorand others).That way, my controller can be as simple as
The
).
ParamsValidator.validate/1function is the one that will raise the error. This is just a small API project and I don’t handle authentication and permission at all. I see @OvermindDL1’s solution as kind of convoluted but maybe it’s because I just haven’t worked on something of that scale yet (or because I haven’t seen the bigger pictureI do want feedbacks if there is a more recommended approach than using custom errors, or if there are drawbacks by using it.
OvermindDL1
True true, unauthorized should be 401 if you are returning codes.[quote=“bobbypriambodo, post:9, topic:2191”]
).
The ParamsValidator.validate/1 function is the one that will raise the error. This is just a small API project and I don’t handle authentication and permission at all. I see @OvermindDL1’s solution as kind of convoluted but maybe it’s because I just haven’t worked on something of that scale yet (or because I haven’t seen the bigger picture
[/quote]
It could very well be convoluted! ^.^
It grew this way because the error handling has become massive and can come from a variety of parts, like on my example above the
verify_single_recordcall will cause a Match Error on the{:ok, section}if there is not a single record (I.E., it is not found), the special error tag it returns it matched for in my error handlers to return an appropriate NotFound code or so:I have a long list here of various generic things that can be returned from controllers or functions they call. The not found and unprocessable entities, like most, are more generic than my unauthorized (which redirects to let them login better):
And is probably more of what the OP wants to do.