conduit
conduit copied to clipboard
How can one authorise queries rather than commands?
I've learned command authorisation by reading your article "Building a CQRS/ES web application in Elixir using Phoenix". However, I want to perform query authorisation in a bounded context. Could you give me some hints about how to do that?
Cheers, Zhen Zhang
@zhangzhen You could apply a similar approach to queries by using an authorisation library, like Canada, but define the permissions based upon your query modules.
Define each available query in its own module:
defmodule MyApp.ExampleQuery do
import Ecto.Query, only: [from: 2]
alias MyApp.ExampleProjection
def new(user_id) do
from e in ExampleProjection,
where: e.owner_id == ^user_id
end
end
Configure the permissions for each query:
defimpl Canada.Can, for: MyApp.User do
alias MyApp.User
alias MyApp.ExampleQuery
def can?(%User{} = user, :execute, %ExampleQuery{} = query) do
# .. is user authorised?
end
def can?(_user, _action, _query), do: false
end
Then in your Phoenix controller, or context module, ensure the user is authorised to execute the query:
defmodule MyApp.Example do
alias MyApp.ExampleQuery
alias MyApp.Repo
def query(user) do
query = ExampleQuery.new(user.id)
if can?(user, :execute, query) do
{:ok, Repo.all(query)}
else
{:error, unauthorised}
end
end
@slashdotdash Thank you for your reply. What file should be the implementation of Canada.Can protocol put in? By the way I have two questions about Segment Challenge.
- What does SegmentChallenge.Authorisation.User look like?
- What is the difference between SegmentChallenge.Authorisation.User and SegmentChallenge.Challenges.Projections.User?
Thanks a lot!
Zhen
What file should be the implementation of Canada.Can protocol put in?
I have a separate "authorisation" app inside my umbrella that contains the policies, split by aggregate:
apps/authorisation/lib/policies/challenge_policy.exapps/authorisation/lib/policies/stage_policy.ex
There's an authorisation.ex file containing the base Canada.Can protocol implementation which delegates to the appropriate policy module above.
What does
SegmentChallenge.Authorisation.Userlook like?
It's just a struct containing the user's identity.
defmodule SegmentChallenge.Authorisation.User do
defstruct [
:athlete_uuid,
]
end
What is the difference between
SegmentChallenge.Authorisation.UserandSegmentChallenge.Challenges.Projections.User?
In Segment Challenge there is no user projection as authentication is provided via Strava. Instead I simply create the above User struct from the athelte's identity returned by Strava after successful authentication.
If you have your own user projection then that can be used for the authorisation checks.
@slashdotdash I love your articles at Binary Consulting and your book "Building Conduit". Recently I've read the article "Using Elixir's GenStage and Flow to build product recommendations". This article inspires me to build a bioinformatics pipeline using genstage and flow. Since a bioinformatics pipeline consists of programs written in different languages, such as c++, python, java, it seems more complicated than a normal computational pipeline. Rewriting such programs in elixir is nearly impossible in a short time. For example, a task in a pipeline aligns millions of reads to the reference sequence by running an external program in multithreaded mode, and is therefore CPU-intensive. Can it be implemented as a stage in Genstage.Flow? Would you please give me some hints?
By the time, is there another way to ask you questions besides here?
Best, Zhen
@zhangzhen Feel free to contact via email ([email protected]).
I'm sure you could use GenStage Flow for that scenario. My only advice would be to ensure you configure max_demand (and possibly stages) appropriately for the pipeline to optimise memory usage and throughput. The best way to determine those figures is by instrumenting and tuning them.