conduit icon indicating copy to clipboard operation
conduit copied to clipboard

How can one authorise queries rather than commands?

Open zhangzhen opened this issue 7 years ago • 5 comments

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 avatar Dec 29 '17 05:12 zhangzhen

@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 avatar Dec 30 '17 21:12 slashdotdash

@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.

  1. What does SegmentChallenge.Authorisation.User look like?
  2. What is the difference between SegmentChallenge.Authorisation.User and SegmentChallenge.Challenges.Projections.User?

Thanks a lot!

Zhen

zhangzhen avatar Dec 31 '17 09:12 zhangzhen

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.ex
  • apps/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.User look 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.User and SegmentChallenge.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 avatar Dec 31 '17 13:12 slashdotdash

@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 avatar Jan 04 '18 06:01 zhangzhen

@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.

slashdotdash avatar Jan 04 '18 10:01 slashdotdash