Guy14

Guy14

Hello!!

I have a UserEmotion Resource (it’s a join between User resource and Emotion resource).

In my User resource, I allow only users with role == :admin to access the :read action:

policy action(:read) do
      authorize_if actor_attribute_equals(:role, :admin)
    end

The UserEmotion exposes a create function named add_user_emotion which is quite basic:

    create :add_user_emotion do
      description "Adds a new entry to user_emotions."

      argument :user, :uuid do
        allow_nil? false
      end

      argument :emotion, :uuid do
        allow_nil? false
      end

      change manage_relationship(:user, type: :append)
      change manage_relationship(:emotion, type: :append)
    end

I want this function to be available for all Users, including those who are not :admin (maybe I’ll populate the :user myself from the actor to ensure a User only adds Emotions for itself).

To fix the permission issue, I could:

  • create a :list function in User resource, and I expose this function instead of :read in the API, so I put restricting policies on :list and I open all permissions on :read → it doesn’t feel the right way to me, since there’s a breach if I later expose the :read function by mistake
    OR
  • create an intermediary function before the :add_user_emotion that will set an actor like actor: %{internal: true}, and then authorize this internal new actor in the policy like so:
policy action(:read) do
      authorize_if actor_attribute_equals(:role, :admin)
      authorize_if actor_attribute_equals(:internal, :true) // NEW ACTOR
    end

None of my solutions seems good to me. Do you have any advice on this case? I guess it’s a common challenge, but I haven’t found a solution while checking at the docs (but I found many responses to other questions I had :slightly_smiling_face: ).

P.S. I’ve also tried the accessing_from builtin policy, but It doesn’t work since my UserEmotion Resource is not yet created but on the path to be created…

    policy action(:read) do
      authorize_if actor_attribute_equals(:role, :admin)
      authorize_if accessing_from(UserEmotion, :emotions)
    end

Showing Posts 1 to 8

Guy14

Guy14 OP

I found a better way to do what I want: it looks like this

# in UserEmotion Resource

  actions do
    create :add_user_emotion do
      description "Adds a new entry to user_emotions."

      change relate_actor(:user)

      argument :emotion, :uuid do
        allow_nil? false
      end

      change manage_relationship(:emotion, type: :append)

      change load(:emotion)
    end

   […]
  end

This way I don’t need to retrieve the User from a given uuid, and it logically allows only the current actor to manage its own Emotions.

I’m still curious if there is a way to allow the current action (once it’s authorized) to call other internal actions that would not directly be authorized for the initial actor.

Maybe it would be a bad design since it would be a way to retrieve data that is not supposed to be callable by the inital actor?
But so is there a trick to keep a way of retrieving User for instance from their uuid to link them to a Resource under creation? (my first “solution” works, after all… but I’m not sure of it’s legitimacy)

zachdaniel

zachdaniel

Creator of Ash

You can pass an authorize?: false option to manage_relationship if you want the parent action’s authorization rules to be the arbiter in this context. That might have helped with your original design.

Guy14

Guy14 OP

Oh yeah, that’s great, I’ll keep this one close :slight_smile:
Thanks a lot!

sevenseacat

sevenseacat

Author of Ash Framework

In this case you might also want an authorization rule for users, to let them read their own records. ie. authorize_if expr(id == ^actor(:id))

I think you still need to be able to read the user record, to use relate_actor!

Guy14

Guy14 OP

Thanks a lot for the precision @sevenseacat !!

To give you my feedback: the relate_actor works, even if the actor has no rights on the primary User’s :read function.

I guess it’s because the actor is already a User Resource in the context, rather than being only an uuid, and that Ash needs to retrieve the Resource to link tho User Resource to the UserEmotion.

FlyingNoodle

FlyingNoodle

My feeling says use relate_actor and then if you need an admin to set any user you do something like:

create :add_user_emotion_admin do
  argument :emotion, :uuid, allow_nil?: false
  argument :user, :uuid, allow_nil?: false
  change manage_relationship(:emotion, type: :append)
  change manage_relationship(:user, type: :append)
end

policies
  action(:add_user_emotion_admin) do
    authorize_if actor_attribute_equals(:admin, true)
  end
end
sevenseacat

sevenseacat

Author of Ash Framework

Gotcha. I just did some experimenting, and it seems it will work on create actions, and atomic update actions (it’ll fetch the attribute from the User resource) - but for non-atomic update actions, it won’t :thinking:

Guy14

Guy14 OP

Yeah, seems like a safe and practical way to do what I want :slight_smile:

@sevenseacat Thanks for this new feedback! So it feels safer to add the policy you gave me: authorize_if expr(id == ^actor(:id)) on the User’s :read action.

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews