Advice for policy simplicity

Hey, I'm just getting into policies and wanted some advice on how to make this easy to work with. So I want all code interface actions to permit calling any function from the application itself or IEx. But I want all external callers to be verified for access. Is this a good start?
elixir
policies do
default_access_type :strict

policy actor_present() do
authorize_if actor_attribute_equals(:admin?, true)
end

policy actor_absent() do
authorize_if always()
end
end
elixir
policies do
default_access_type :strict

policy actor_present() do
authorize_if actor_attribute_equals(:admin?, true)
end

policy actor_absent() do
authorize_if always()
end
end
If I understand it correctly, this means that if an actor is absent or authorize? is false, the caller is authorized. If an actor is present we check if they are an admin. The Admin UI always disables authorization I think, and if I don't give any extra arguments in my code interface functions then they will go through too. Is this a good design? What is the performance impact? Or would it better to make separate actions that are only exposed externally and not in the code interface and authorize only those?
Solution:
This option: define :foo, default_options: [authorize?: false]
Jump to solution
11 Replies
ZachDaniel
ZachDaniel•3mo ago
🤔 Out of curiosity, why do you want all code interface calls to automatically authorize? Is it because everything is either an API or its internal? If so you could do something like this instead:
define :foo, default_options: [authorize?: false]
define :foo, default_options: [authorize?: false]
Sienhopist
SienhopistOP•3mo ago
Mainly because I think the app should have access to everything it needs internally
ZachDaniel
ZachDaniel•3mo ago
Yeah but your app can always provide authorize?: false on its calls to resources too Then you have something explicit that is bypassing authorization, instead of something implicit Also this:
policy actor_absent() do
authorize_if always()
end
policy actor_absent() do
authorize_if always()
end
Is very scary it means if somehow someone made it past your authentication layer to call actions w/o a user, that they will be allowed to do whatever they want
Sienhopist
SienhopistOP•3mo ago
Yeah that's a good point. I'm mainly trying to figure out what is a good balance. Obviously all external requests should be authorized (unless coming from the admin UI), but I also didn't want to have to deal with authorization from within the code itself...
ZachDaniel
ZachDaniel•3mo ago
There are various other options using authorize?: false on your internal calls Alternatively, you can have a special type of actor that you set for internal calls actor: %SystemUser{}
Sienhopist
SienhopistOP•3mo ago
I'm not discounting that this is just a me problem. Maybe this is the wrong perspective and authorization should always be explicitly disabled or visible 🤔
Solution
ZachDaniel
ZachDaniel•3mo ago
This option: define :foo, default_options: [authorize?: false]
ZachDaniel
ZachDaniel•3mo ago
is a middle ground to me A bit annoying to add that to all your code interface calls But makes it perfectly clear what does/doesn't authorize in your system with something that is easy to change on a case-by-case basis.
Sienhopist
SienhopistOP•3mo ago
Yeah thanks for that. I'll stick to that for now and see how it feels. I'd like to keep things simple internally, but ofc without accidentally exposing something externally without authorization 😅.
Sienhopist
SienhopistOP•3mo ago
Speaking of which, how do you check the type or struct of the actor? Do we just check the hidden __struct__ field?
ZachDaniel
ZachDaniel•3mo ago
Pretty much, yes well, I'd make a custom check and pattern match in the simple check

Did you find this page helpful?