Giter Club home page Giter Club logo

Comments (5)

yordis avatar yordis commented on July 1, 2024

@egeersoz create another table where you do the join with this table.

Tenants --- Has Many --- Tenant Tokens --- Belongs To --- Guardian Tokens

Let me know if you get the idea

from guardian_db.

egeersoz avatar egeersoz commented on July 1, 2024

Hi @yordis

I actually tried that before opening this issue. Here's the migration:

defmodule MyApp.Repo.Migrations.Guardian.UserTokens do
  use Ecto.Migration

  def change do
    create table(:user_tokens) do
      add :jti, references(:guardian_tokens, column: :jti)
      add :user_id, :integer
      add :tenant, :string

      timestamps(type: :utc_datetime_usec)
    end
  end
end

Here's the resulting error:

(Postgrex.Error) ERROR 42830 (invalid_foreign_key) there is no unique constraint matching given keys for referenced table "guardian_tokens"

Here's what I ended up doing:

Added add(:tenant, :text) to guardian_tokens migration.

Added the following in my context:

  def update_token_with_tenant(tenant, sub, jwt) do
    query = from g in GuardianToken,
            where: g.jwt == ^jwt and g.sub == ^sub
    Repo.update_all(query, set: [tenant: tenant])
  end

Added the above function call inside after_encode_and_sign callback:

def after_encode_and_sign(resource, claims, token, _options) do
    with {:ok, {_resource, _type, _claims, jwt}} <- Guardian.DB.after_encode_and_sign(resource, claims["typ"], claims, token) do
      Accounts.update_token_with_tenant(claims["tenant"], claims["sub"], jwt)
      {:ok, token}
    end
  end

After doing all this, I can delete all of the user's tokens (e.g. when disabling their account in my app's admin console), with this:

def delete_all_tokens(tenant, user_id) do
    sub = to_string(user_id)
    query = from g in GuardianToken,
            where: g.sub == ^sub and g.tenant == ^tenant
    Repo.delete_all(query)
  end

This seems to work, but I haven't deployed it to production yet.

Anyways, the reason I opened this issue is that I feel that this library can handle multi-schema databases better, specifically the scenario where the 'guardian_tokens' table will be listed under the public schema, but the users that are referred to in the sub column are in tables under tenant schemas. I already encode the tenant in the claims, so that part works fine, but we also need to be able to query the 'guardian_tokens' table based on the tenant, and Postgres doesn't have a performant way to query JSON (e.g. JSON properties aren't indexed).

I propose this issue be reopened so we can use it for discussion.

from guardian_db.

yordis avatar yordis commented on July 1, 2024

(Postgrex.Error) ERROR 42830 (invalid_foreign_key) there is no unique constraint matching given keys for referenced table "guardian_tokens"

Based on the migration

add(:jti, :string, primary_key: true)
add(:aud, :string, primary_key: true)
it seems that the key is a combination.

Anyways, the reason I opened this issue is that I feel that this library can handle multi-schema databases better,

I don't believe that adding more complexity to the package based on specific business use case like your is not good. The table is normalized enough to allow people to extend the database relationship. The intention of this package is not to take those business decisions.

What you did is just fine to do for those who need it to do this.

Everyone does the multi-tenant in their way, and I am really against to take a decision based on your setup.

from guardian_db.

egeersoz avatar egeersoz commented on July 1, 2024

I don't think the way I did it is "good", because now each time a token is issued, I perform two queries, once to insert the token, and then go back and update its tenant column in 'guardian_tokens'. There needs to be a way to do this using a single query, i.e. when GuardianDB performs the initial insert.

Regarding your broader point, in my opinion you should consider making this library flexible enough to accommodate different use cases, rather than building it for the most basic use case, with incorrect or incomplete assumptions about the user's database design, and expecting them to come up with clever hacks or complex workarounds to make it do what they need.

In addition, my use case is not rare at all. Indeed, multi-schema databases are the recommended way to do multi-tenancy in Postgres. This is how various multi-tenancy libraries like Apartmentex (based on Ruby's Apartment gem), Tenantex and Triplex work. If you take a quick look at those, you'll note that every single one uses a multi-schema approach for Postgres. GuardianDB, as it currently stands, is not flexible enough to accommodate that approach.

from guardian_db.

yordis avatar yordis commented on July 1, 2024

PR welcome, that would help to keep the discussion based on the implementation.

The main issue is that you are crossing boundaries just because you can reach for the database table, that is the whole problem of having shared database for services, the first thing people do is to reach out for the database to bypass the services.

Sorry, just because you can don't mean you should.

I totally understand your point of views, but use the service boundaries for what you are doing.

from guardian_db.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.