From c56b9585ecd8201b710805812f7abbb6a475bfc8 Mon Sep 17 00:00:00 2001 From: Grant Zvolsky Date: Wed, 6 Apr 2022 15:29:34 +0200 Subject: [PATCH] fix: mysql slice delete - Add a workaround for [mysql slice delete](https://github.com/gobuffalo/pop/issues/699) - Optimize logout verification (save 1 db rountrip) - Update a test to use StaticContextualizer & revert CleanAndMigrate workaround - Ensure a Client generated with faker satisfies the DB schema - Remove unused argument from HandleConsentRequest --- client/client.go | 10 ++++----- client/manager_test_helpers.go | 4 +++- consent/handler.go | 4 ++-- consent/handler_test.go | 2 +- consent/manager.go | 2 +- consent/manager_test_helpers.go | 16 +++++++------- consent/sdk_test.go | 8 +++---- driver/registry_base.go | 2 +- internal/driver.go | 7 ------- internal/testhelpers/janitor_test_helper.go | 6 +++--- oauth2/fosite_store_helpers.go | 2 +- oauth2/oauth2_refresh_token_test.go | 3 ++- persistence/sql/persister_consent.go | 21 ++++++++----------- persistence/sql/persister_oauth2.go | 9 +++++++- persistence/sql/persister_test.go | 8 +++++-- .../20220210000001000000_nid.sqlite.up.sql | 3 ++- 16 files changed, 56 insertions(+), 51 deletions(-) diff --git a/client/client.go b/client/client.go index 4ad6b250a37..7092fdf5983 100644 --- a/client/client.go +++ b/client/client.go @@ -125,7 +125,7 @@ type Client struct { // SubjectType requested for responses to this Client. The subject_types_supported Discovery parameter contains a // list of the supported subject_type values for this server. Valid types include `pairwise` and `public`. - SubjectType string `json:"subject_type" db:"subject_type"` + SubjectType string `json:"subject_type" db:"subject_type" faker:"len=15"` // URL using the https scheme to be used in calculating Pseudonymous Identifiers by the OP. The URL references a // file with a single JSON array of redirect_uri values. @@ -152,10 +152,10 @@ type Client struct { // Requested Client Authentication method for the Token Endpoint. The options are client_secret_post, // client_secret_basic, private_key_jwt, and none. - TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty" db:"token_endpoint_auth_method"` + TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty" db:"token_endpoint_auth_method" faker:"len=25"` // Requested Client Authentication signing algorithm for the Token Endpoint. - TokenEndpointAuthSigningAlgorithm string `json:"token_endpoint_auth_signing_alg,omitempty" db:"token_endpoint_auth_signing_alg"` + TokenEndpointAuthSigningAlgorithm string `json:"token_endpoint_auth_signing_alg,omitempty" db:"token_endpoint_auth_signing_alg" faker:"len=10"` // Array of request_uri values that are pre-registered by the RP for use at the OP. Servers MAY cache the // contents of the files referenced by these URIs and not retrieve them at the time they are used in a request. @@ -165,12 +165,12 @@ type Client struct { // JWS [JWS] alg algorithm [JWA] that MUST be used for signing Request Objects sent to the OP. All Request Objects // from this Client MUST be rejected, if not signed with this algorithm. - RequestObjectSigningAlgorithm string `json:"request_object_signing_alg,omitempty" db:"request_object_signing_alg"` + RequestObjectSigningAlgorithm string `json:"request_object_signing_alg,omitempty" db:"request_object_signing_alg" faker:"len=10"` // JWS alg algorithm [JWA] REQUIRED for signing UserInfo Responses. If this is specified, the response will be JWT // [JWT] serialized, and signed using JWS. The default, if omitted, is for the UserInfo Response to return the Claims // as a UTF-8 encoded JSON object using the application/json content-type. - UserinfoSignedResponseAlg string `json:"userinfo_signed_response_alg,omitempty" db:"userinfo_signed_response_alg"` + UserinfoSignedResponseAlg string `json:"userinfo_signed_response_alg,omitempty" db:"userinfo_signed_response_alg" faker:"len=10"` // CreatedAt returns the timestamp of the client's creation. CreatedAt time.Time `json:"created_at,omitempty" db:"created_at"` diff --git a/client/manager_test_helpers.go b/client/manager_test_helpers.go index 03b49d54860..b99519cbf35 100644 --- a/client/manager_test_helpers.go +++ b/client/manager_test_helpers.go @@ -132,7 +132,7 @@ func TestHelperCreateGetUpdateDeleteClientNext(t *testing.T, m Storage, networks t.Run(fmt.Sprintf("nid=%s", nid), func(t *testing.T) { var client Client require.NoError(t, faker.FakeData(&client)) - client.CreatedAt = time.Now() + client.CreatedAt = time.Now().Truncate(time.Second).UTC() t.Run("lifecycle=does not exist", func(t *testing.T) { _, err := m.GetClient(ctx, "1234") @@ -148,6 +148,7 @@ func TestHelperCreateGetUpdateDeleteClientNext(t *testing.T, m Storage, networks assertx.EqualAsJSONExcept(t, &client, c, []string{ "registration_access_token", "registration_client_uri", + "updated_at", }) n, err := m.CountClients(ctx) @@ -165,6 +166,7 @@ func TestHelperCreateGetUpdateDeleteClientNext(t *testing.T, m Storage, networks assertx.EqualAsJSONExcept(t, &client, c, []string{ "registration_access_token", "registration_client_uri", + "updated_at", }) resources[nid] = append(resources[nid], client) }) diff --git a/consent/handler.go b/consent/handler.go index 9c4df357f3b..02e18f40b88 100644 --- a/consent/handler.go +++ b/consent/handler.go @@ -574,7 +574,7 @@ func (h *Handler) AcceptConsentRequest(w http.ResponseWriter, r *http.Request, p p.RequestedAt = cr.RequestedAt p.HandledAt = sqlxx.NullTime(time.Now().UTC()) - hr, err := h.r.ConsentManager().HandleConsentRequest(r.Context(), challenge, &p) + hr, err := h.r.ConsentManager().HandleConsentRequest(r.Context(), &p) if err != nil { h.r.Writer().WriteError(w, r, errorsx.WithStack(err)) return @@ -651,7 +651,7 @@ func (h *Handler) RejectConsentRequest(w http.ResponseWriter, r *http.Request, p return } - request, err := h.r.ConsentManager().HandleConsentRequest(r.Context(), challenge, &HandledConsentRequest{ + request, err := h.r.ConsentManager().HandleConsentRequest(r.Context(), &HandledConsentRequest{ Error: &p, ID: challenge, RequestedAt: hr.RequestedAt, diff --git a/consent/handler_test.go b/consent/handler_test.go index 2f20cf96365..1a6d1020bc8 100644 --- a/consent/handler_test.go +++ b/consent/handler_test.go @@ -191,7 +191,7 @@ func TestGetConsentRequest(t *testing.T) { })) if tc.handled { - _, err := reg.ConsentManager().HandleConsentRequest(context.Background(), challenge, &HandledConsentRequest{ + _, err := reg.ConsentManager().HandleConsentRequest(context.Background(), &HandledConsentRequest{ ID: challenge, WasHandled: true, HandledAt: sqlxx.NullTime(time.Now()), diff --git a/consent/manager.go b/consent/manager.go index 3d6f690888e..9c0fcf7b7d4 100644 --- a/consent/manager.go +++ b/consent/manager.go @@ -42,7 +42,7 @@ func (_ ForcedObfuscatedLoginSession) TableName() string { type Manager interface { CreateConsentRequest(ctx context.Context, req *ConsentRequest) error GetConsentRequest(ctx context.Context, challenge string) (*ConsentRequest, error) - HandleConsentRequest(ctx context.Context, challenge string, r *HandledConsentRequest) (*ConsentRequest, error) + HandleConsentRequest(ctx context.Context, r *HandledConsentRequest) (*ConsentRequest, error) RevokeSubjectConsentSession(ctx context.Context, user string) error RevokeSubjectClientConsentSession(ctx context.Context, user, client string) error diff --git a/consent/manager_test_helpers.go b/consent/manager_test_helpers.go index 234ed1d06fb..9606b16ef12 100644 --- a/consent/manager_test_helpers.go +++ b/consent/manager_test_helpers.go @@ -197,7 +197,7 @@ func SaneMockHandleConsentRequest(t *testing.T, m Manager, c *ConsentRequest, au HandledAt: sqlxx.NullTime(time.Now().UTC().Add(-time.Minute)), } - _, err := m.HandleConsentRequest(context.Background(), c.ID, h) + _, err := m.HandleConsentRequest(context.Background(), h) require.NoError(t, err) return h } @@ -487,13 +487,13 @@ func ManagerTests(m Manager, clientManager client.Manager, fositeManager x.Fosit compareConsentRequest(t, c, got1) assert.False(t, got1.WasHandled) - got1, err = m.HandleConsentRequest(context.Background(), consentChallenge, h) + got1, err = m.HandleConsentRequest(context.Background(), h) require.NoError(t, err) require.Equal(t, time.Now().UTC().Round(time.Minute), time.Time(h.HandledAt).Round(time.Minute)) compareConsentRequest(t, c, got1) h.GrantedAudience = sqlxx.StringSlicePipeDelimiter{"new-audience"} - _, err = m.HandleConsentRequest(context.Background(), consentChallenge, h) + _, err = m.HandleConsentRequest(context.Background(), h) require.NoError(t, err) got2, err := m.VerifyAndInvalidateConsentRequest(context.Background(), makeID("verifier", tenant, tc.key)) @@ -504,7 +504,7 @@ func ManagerTests(m Manager, clientManager client.Manager, fositeManager x.Fosit // Trying to update this again should return an error because the consent request was used. h.GrantedAudience = sqlxx.StringSlicePipeDelimiter{"new-audience", "new-audience-2"} - _, err = m.HandleConsentRequest(context.Background(), consentChallenge, h) + _, err = m.HandleConsentRequest(context.Background(), h) require.Error(t, err) if tc.hasError { @@ -605,9 +605,9 @@ func ManagerTests(m Manager, clientManager client.Manager, fositeManager x.Fosit require.NoError(t, m.CreateConsentRequest(context.Background(), cr1)) require.NoError(t, m.CreateConsentRequest(context.Background(), cr2)) - _, err := m.HandleConsentRequest(context.Background(), challengerv1, hcr1) + _, err := m.HandleConsentRequest(context.Background(), hcr1) require.NoError(t, err) - _, err = m.HandleConsentRequest(context.Background(), challengerv2, hcr2) + _, err = m.HandleConsentRequest(context.Background(), hcr2) require.NoError(t, err) require.NoError(t, fositeManager.CreateAccessTokenSession(context.Background(), makeID("", tenant, "trva1"), &fosite.Request{Client: cr1.Client, ID: challengerv1, RequestedAt: time.Now()})) @@ -678,9 +678,9 @@ func ManagerTests(m Manager, clientManager client.Manager, fositeManager x.Fosit require.NoError(t, m.CreateConsentRequest(context.Background(), cr1)) require.NoError(t, m.CreateConsentRequest(context.Background(), cr2)) - _, err := m.HandleConsentRequest(context.Background(), challengerv1, hcr1) + _, err := m.HandleConsentRequest(context.Background(), hcr1) require.NoError(t, err) - _, err = m.HandleConsentRequest(context.Background(), challengerv2, hcr2) + _, err = m.HandleConsentRequest(context.Background(), hcr2) require.NoError(t, err) for i, tc := range []struct { diff --git a/consent/sdk_test.go b/consent/sdk_test.go index d46e0cf555c..aa10f6d2665 100644 --- a/consent/sdk_test.go +++ b/consent/sdk_test.go @@ -101,13 +101,13 @@ func TestSDK(t *testing.T) { require.NoError(t, m.CreateConsentRequest(context.Background(), cr2)) require.NoError(t, m.CreateConsentRequest(context.Background(), cr3)) require.NoError(t, m.CreateConsentRequest(context.Background(), cr4)) - _, err := m.HandleConsentRequest(context.Background(), makeID("challenge", tenant, "1"), hcr1) + _, err := m.HandleConsentRequest(context.Background(), hcr1) require.NoError(t, err) - _, err = m.HandleConsentRequest(context.Background(), makeID("challenge", tenant, "2"), hcr2) + _, err = m.HandleConsentRequest(context.Background(), hcr2) require.NoError(t, err) - _, err = m.HandleConsentRequest(context.Background(), makeID("challenge", tenant, "3"), hcr3) + _, err = m.HandleConsentRequest(context.Background(), hcr3) require.NoError(t, err) - _, err = m.HandleConsentRequest(context.Background(), makeID("challenge", tenant, "4"), hcr4) + _, err = m.HandleConsentRequest(context.Background(), hcr4) require.NoError(t, err) lur1 := MockLogoutRequest("testsdk-1", true, tenant) diff --git a/driver/registry_base.go b/driver/registry_base.go index ced09c3085c..bc9e7dbebca 100644 --- a/driver/registry_base.go +++ b/driver/registry_base.go @@ -188,7 +188,7 @@ func (m *RegistryBase) AuditLogger() *logrusx.Logger { func (m *RegistryBase) ClientHasher() fosite.Hasher { if m.fh == nil { - if m.Tracer(context.TODO()).IsLoaded() { + if m.Tracer(contextx.RootContext).IsLoaded() { m.fh = &tracing.TracedBCrypt{WorkFactor: m.Config(contextx.RootContext).BCryptCost()} } else { m.fh = x.NewBCrypt(m.Config(contextx.RootContext)) diff --git a/internal/driver.go b/internal/driver.go index 01febd4a646..005ee9ff7dc 100644 --- a/internal/driver.go +++ b/internal/driver.go @@ -7,7 +7,6 @@ import ( "testing" "github.com/ory/x/configx" - "github.com/ory/x/networkx" "github.com/stretchr/testify/require" @@ -78,14 +77,8 @@ func newRegistryDefault(t *testing.T, url string, c *config.Provider, migrate bo func CleanAndMigrate(reg driver.Registry) func(*testing.T) { return func(t *testing.T) { - net := &networkx.Network{} - recreateNetwork := reg.Persister().Connection(context.Background()).First(net) == nil x.CleanSQLPop(t, reg.Persister().Connection(context.Background())) require.NoError(t, reg.Persister().MigrateUp(context.Background())) - if recreateNetwork { - require.NoError(t, reg.Persister().Connection(context.Background()).RawQuery("DELETE FROM networks").Exec()) - require.NoError(t, reg.Persister().Connection(context.Background()).Create(net)) - } t.Log("clean and migrate done") } } diff --git a/internal/testhelpers/janitor_test_helper.go b/internal/testhelpers/janitor_test_helper.go index 907138b8c00..ae4ffbec226 100644 --- a/internal/testhelpers/janitor_test_helper.go +++ b/internal/testhelpers/janitor_test_helper.go @@ -274,12 +274,12 @@ func (j *JanitorConsentTestHelper) ConsentRejectionSetup(ctx context.Context, cm for _, r := range j.flushConsentRequests { if r.ID == j.flushConsentRequests[0].ID { // accept this one - _, err = cm.HandleConsentRequest(ctx, r.ID, consent.NewHandledConsentRequest( + _, err = cm.HandleConsentRequest(ctx, consent.NewHandledConsentRequest( r.ID, false, r.RequestedAt, r.AuthenticatedAt)) require.NoError(t, err) continue } - _, err = cm.HandleConsentRequest(ctx, r.ID, consent.NewHandledConsentRequest( + _, err = cm.HandleConsentRequest(ctx, consent.NewHandledConsentRequest( r.ID, true, r.RequestedAt, r.AuthenticatedAt)) require.NoError(t, err) } @@ -362,7 +362,7 @@ func (j *JanitorConsentTestHelper) ConsentTimeoutSetup(ctx context.Context, cm c } // Create at least 1 consent request that has been accepted - _, err = cm.HandleConsentRequest(ctx, j.flushConsentRequests[0].ID, &consent.HandledConsentRequest{ + _, err = cm.HandleConsentRequest(ctx, &consent.HandledConsentRequest{ ID: j.flushConsentRequests[0].ID, WasHandled: true, HandledAt: sqlxx.NullTime(time.Now()), diff --git a/oauth2/fosite_store_helpers.go b/oauth2/fosite_store_helpers.go index 9e7e7d8ff9f..675ec82c8b0 100644 --- a/oauth2/fosite_store_helpers.go +++ b/oauth2/fosite_store_helpers.go @@ -153,7 +153,7 @@ func mockRequestForeignKey(t *testing.T, id string, x InternalRegistry, createCl require.NoError(t, x.ConsentManager().CreateLoginRequest(context.Background(), &consent.LoginRequest{Client: cl, OpenIDConnectContext: new(consent.OpenIDConnectContext), ID: id, Verifier: id, AuthenticatedAt: sqlxx.NullTime(time.Now()), RequestedAt: time.Now()})) require.NoError(t, x.ConsentManager().CreateConsentRequest(context.Background(), cr)) - _, err := x.ConsentManager().HandleConsentRequest(context.Background(), id, &consent.HandledConsentRequest{ + _, err := x.ConsentManager().HandleConsentRequest(context.Background(), &consent.HandledConsentRequest{ ConsentRequest: cr, Session: new(consent.ConsentRequestSessionData), AuthenticatedAt: sqlxx.NullTime(time.Now()), ID: id, RequestedAt: time.Now(), diff --git a/oauth2/oauth2_refresh_token_test.go b/oauth2/oauth2_refresh_token_test.go index 73e573eedef..465abdcce34 100644 --- a/oauth2/oauth2_refresh_token_test.go +++ b/oauth2/oauth2_refresh_token_test.go @@ -18,6 +18,7 @@ import ( "github.com/ory/fosite" hc "github.com/ory/hydra/client" "github.com/ory/hydra/driver" + "github.com/ory/hydra/internal" "github.com/ory/hydra/oauth2" "github.com/ory/hydra/x/contextx" "github.com/ory/x/dbal" @@ -84,7 +85,7 @@ func TestCreateRefreshTokenSessionStress(t *testing.T) { } net := &networkx.Network{} require.NoError(t, dbRegistry.Persister().Connection(context.Background()).First(net)) - dbRegistry.WithContextualizer(&contextx.StaticContextualizer{NID: net.ID}) + dbRegistry.WithContextualizer(&contextx.StaticContextualizer{NID: net.ID, C: internal.NewConfigurationWithDefaults()}) ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) require.NoError(t, dbRegistry.OAuth2Storage().(clientCreator).CreateClient(ctx, &testClient)) diff --git a/persistence/sql/persister_consent.go b/persistence/sql/persister_consent.go index 641605f33b0..03b2c868d74 100644 --- a/persistence/sql/persister_consent.go +++ b/persistence/sql/persister_consent.go @@ -225,7 +225,7 @@ func (p *Persister) GetLoginRequest(ctx context.Context, login_challenge string) }) } -func (p *Persister) HandleConsentRequest(ctx context.Context, challenge string, r *consent.HandledConsentRequest) (*consent.ConsentRequest, error) { +func (p *Persister) HandleConsentRequest(ctx context.Context, r *consent.HandledConsentRequest) (*consent.ConsentRequest, error) { f := &flow.Flow{} if err := sqlcon.HandleError(p.QueryWithNetwork(ctx).Where("consent_challenge_id = ?", r.ID).First(f)); errors.Is(err, sqlcon.ErrNoRows) { @@ -496,24 +496,21 @@ func (p *Persister) GetLogoutRequest(ctx context.Context, challenge string) (*co func (p *Persister) VerifyAndInvalidateLogoutRequest(ctx context.Context, verifier string) (*consent.LogoutRequest, error) { var lr consent.LogoutRequest return &lr, p.transaction(ctx, func(ctx context.Context, c *pop.Connection) error { - if err := p.QueryWithNetwork(ctx).Where("verifier=? AND was_used=FALSE AND accepted=TRUE AND rejected=FALSE", verifier).Select("challenge").First(&lr); err != nil { - if err == sql.ErrNoRows { - return errorsx.WithStack(x.ErrNotFound) - } - - return sqlcon.HandleError(err) - } - - if err := c.RawQuery("UPDATE hydra_oauth2_logout_request SET was_used=TRUE WHERE verifier=? AND nid = ?", verifier, p.NetworkID(ctx)).Exec(); err != nil { + if count, err := c.RawQuery( + "UPDATE hydra_oauth2_logout_request SET was_used=TRUE WHERE nid = ? AND verifier=? AND was_used=FALSE AND accepted=TRUE AND rejected=FALSE", + p.NetworkID(ctx), + verifier, + ).ExecWithCount(); count == 0 && err == nil { + return errorsx.WithStack(x.ErrNotFound) + } else if err != nil { return sqlcon.HandleError(err) } - updated, err := p.GetLogoutRequest(ctx, lr.ID) + err := sqlcon.HandleError(p.QueryWithNetwork(ctx).Where("verifier=?", verifier).First(&lr)) if err != nil { return err } - lr = *updated return nil }) } diff --git a/persistence/sql/persister_oauth2.go b/persistence/sql/persister_oauth2.go index b8f394e1597..86204d99d12 100644 --- a/persistence/sql/persister_oauth2.go +++ b/persistence/sql/persister_oauth2.go @@ -398,7 +398,14 @@ func (p *Persister) flushInactiveTokens(ctx context.Context, notAfter time.Time, } if i != j { - err = p.QueryWithNetwork(ctx).Where("signature in (?)", signatures[i:j]).Delete(&OAuth2RequestSQL{Table: table}) + ss := signatures[i:j] + iss := make([]interface{}, len(ss)) + for i, v := range ss { // Workaround for https://github.com/gobuffalo/pop/issues/699 + iss[i] = v + } + + err = p.QueryWithNetwork(ctx).Where("signature in (?)", iss...).Delete(&OAuth2RequestSQL{Table: table}) + if err != nil { return sqlcon.HandleError(err) } diff --git a/persistence/sql/persister_test.go b/persistence/sql/persister_test.go index e5af555a7e9..4cad19cd211 100644 --- a/persistence/sql/persister_test.go +++ b/persistence/sql/persister_test.go @@ -145,19 +145,23 @@ func TestManagers(t *testing.T) { "memory": internal.NewRegistrySQLFromURL(t, dbal.SQLiteSharedInMemory, true, &contextx.DefaultContextualizer{}), } - tenant2NID, _ := uuid.NewV4() t2registries := map[string]driver.Registry{ "memory": internal.NewRegistrySQLFromURL(t, dbal.SQLiteSharedInMemory, false, &contextx.DefaultContextualizer{}), } if !testing.Short() { - t1registries["postgres"], t1registries["mysql"], t1registries["cockroach"], _ = internal.ConnectDatabases(t, true, &contextx.DefaultContextualizer{}) t2registries["postgres"], t2registries["mysql"], t2registries["cockroach"], _ = internal.ConnectDatabases(t, false, &contextx.DefaultContextualizer{}) + t1registries["postgres"], t1registries["mysql"], t1registries["cockroach"], _ = internal.ConnectDatabases(t, true, &contextx.DefaultContextualizer{}) } + tenant1NID, _ := uuid.NewV4() + tenant2NID, _ := uuid.NewV4() + for k, t1 := range t1registries { t2 := t2registries[k] + require.NoError(t, t1.Persister().Connection(ctx).Create(&networkx.Network{ID: tenant1NID})) require.NoError(t, t2.Persister().Connection(ctx).Create(&networkx.Network{ID: tenant2NID})) + t1.WithContextualizer(&contextx.StaticContextualizer{NID: tenant1NID, C: t1.Config(ctx)}) t2.WithContextualizer(&contextx.StaticContextualizer{NID: tenant2NID, C: t2.Config(ctx)}) t.Run("parallel-boundary", func(t *testing.T) { testRegistry(t, ctx, k, t1, t2) }) } diff --git a/persistence/sql/src/20220210000001_nid/20220210000001000000_nid.sqlite.up.sql b/persistence/sql/src/20220210000001_nid/20220210000001000000_nid.sqlite.up.sql index 319ceda9862..dc59bbd4f00 100644 --- a/persistence/sql/src/20220210000001_nid/20220210000001000000_nid.sqlite.up.sql +++ b/persistence/sql/src/20220210000001_nid/20220210000001000000_nid.sqlite.up.sql @@ -4,7 +4,8 @@ UPDATE hydra_oauth2_jti_blacklist SET nid = (SELECT id FROM networks LIMIT 1); CREATE TABLE "_hydra_oauth2_jti_blacklist_tmp" ( signature VARCHAR(64) NOT NULL PRIMARY KEY, expires_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - nid CHAR(36) NOT NULL + nid CHAR(36) NOT NULL, + CHECK (nid != '00000000-0000-0000-0000-000000000000') ); INSERT INTO "_hydra_oauth2_jti_blacklist_tmp" (signature, expires_at, nid) SELECT signature, expires_at, nid FROM "hydra_oauth2_jti_blacklist"; DROP TABLE "hydra_oauth2_jti_blacklist";