Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can one authorise queries rather than commands? #6

Open
zhangzhen opened this issue Dec 29, 2017 · 5 comments
Open

How can one authorise queries rather than commands? #6

zhangzhen opened this issue Dec 29, 2017 · 5 comments

Comments

@zhangzhen
Copy link

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

@slashdotdash
Copy link
Owner

slashdotdash commented Dec 30, 2017

@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

@zhangzhen
Copy link
Author

@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

@slashdotdash
Copy link
Owner

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.

@zhangzhen
Copy link
Author

@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

@slashdotdash
Copy link
Owner

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants