Skip to content
Permalink
Browse files

oauth2: Support transactions in SQL store (#1277)

Closes #1247

* vendor: Bump ory/x to 0.29.0 (#1247)
* sql: implements fosite's transactional interface (#1247)
* oauth2: adds test coverage for BeginTX, Commit, and Rollback (#1247)
* vendor: Update crypto dependency
* sql: renamed variables for clarity and modified FositeSqlStore.deleteSession to use transaction  (#1247)
* oauth2: Increased test coverage for Commit and Rollback, improved existing tests (#1247)

Signed-off-by: Michael Wagler <michael.wagler@outlook.com>
  • Loading branch information...
michaelwagler authored and aeneasr committed Feb 7, 2019
1 parent 6ea1665 commit 65415ff731658b822ccd9628d4d497fb6f7634db
Showing with 297 additions and 7 deletions.
  1. +2 −2 go.mod
  2. +4 −0 go.sum
  3. +238 −0 oauth2/fosite_store_helpers.go
  4. +53 −5 oauth2/fosite_store_sql.go
4 go.mod
@@ -20,7 +20,7 @@ require (
github.com/oleiade/reflections v1.0.0
github.com/opentracing/opentracing-go v1.0.2
github.com/ory/dockertest v3.3.2+incompatible
github.com/ory/fosite v0.28.0
github.com/ory/fosite v0.29.0
github.com/ory/go-convenience v0.1.0
github.com/ory/graceful v0.1.0
github.com/ory/herodot v0.4.1
@@ -42,7 +42,7 @@ require (
github.com/urfave/negroni v1.0.0
github.com/ziutek/mymysql v1.5.4 // indirect
go.uber.org/atomic v1.3.2 // indirect
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613
golang.org/x/net v0.0.0-20181029044818-c44066c5c816 // indirect
golang.org/x/oauth2 v0.0.0-20181003184128-c57b0facaced
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
4 go.sum
@@ -160,6 +160,8 @@ github.com/ory/fosite v0.27.4 h1:+2Iu957COQM3vbWp5qjgq0W4icsjbtg+5y3AYJ87EjY=
github.com/ory/fosite v0.27.4/go.mod h1:uttCRNB0lM7+BJFX7CC8Bqo9gAPrcpmA9Ezc80Trwuw=
github.com/ory/fosite v0.28.0 h1:LxCkLXeU5PxYh9d/VbfGVn8GTKkSdOZfrHWdjmIE//c=
github.com/ory/fosite v0.28.0/go.mod h1:uttCRNB0lM7+BJFX7CC8Bqo9gAPrcpmA9Ezc80Trwuw=
github.com/ory/fosite v0.29.0 h1:qFQfwy2YF1Bn5kgilT1LH3N0xOBvV865EXbj2bdxaoY=
github.com/ory/fosite v0.29.0/go.mod h1:0atSZmXO7CAcs6NPMI/Qtot8tmZYj04Nddoold4S2h0=
github.com/ory/go-convenience v0.1.0 h1:zouLKfF2GoSGnJwGq+PE/nJAE6dj2Zj5QlTgmMTsTS8=
github.com/ory/go-convenience v0.1.0/go.mod h1:uEY/a60PL5c12nYz4V5cHY03IBmwIAEm8TWB0yn9KNs=
github.com/ory/graceful v0.1.0 h1:zilpYtcR5vp4GubV4bN2GFJewHaSkMFnnRiJxyH8FAc=
@@ -282,6 +284,8 @@ golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc h1:F5tKCVGp+MUAHhKp5MZtGq
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b h1:Elez2XeF2p9uyVj0yEUDqQ56NFcDtcBNkYP7yv8YbUE=
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 h1:MQ/ZZiDsUapFFiMS+vzwXkCTeEKaum+Do5rINYJDmxc=
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180611182652-db08ff08e862 h1:JZi6BqOZ+iSgmLWe6llhGrNnEnK+YB/MRkStwnEfbqM=
golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -27,6 +27,8 @@ import (
"testing"
"time"

"github.com/ory/fosite/storage"

"github.com/pborman/uuid"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
@@ -118,6 +120,17 @@ func TestHelperRunner(t *testing.T, store ManagerTestSetup, k string) {
t.Helper()
if k != "memory" {
t.Run(fmt.Sprintf("case=testHelperCreateGetDeleteAuthorizeCodes/db=%s", k), testHelperUniqueConstraints(store, k))
t.Run(fmt.Sprint("case=testFositeSqlStoreTransactionsCommitAccessToken"), testFositeSqlStoreTransactionCommitAccessToken(store))
t.Run(fmt.Sprint("case=testFositeSqlStoreTransactionsRollbackAccessToken"), testFositeSqlStoreTransactionRollbackAccessToken(store))
t.Run(fmt.Sprint("case=testFositeSqlStoreTransactionCommitRefreshToken"), testFositeSqlStoreTransactionCommitRefreshToken(store))
t.Run(fmt.Sprint("case=testFositeSqlStoreTransactionRollbackRefreshToken"), testFositeSqlStoreTransactionRollbackRefreshToken(store))
t.Run(fmt.Sprint("case=testFositeSqlStoreTransactionCommitAuthorizeCode"), testFositeSqlStoreTransactionCommitAuthorizeCode(store))
t.Run(fmt.Sprint("case=testFositeSqlStoreTransactionRollbackAuthorizeCode"), testFositeSqlStoreTransactionRollbackAuthorizeCode(store))
t.Run(fmt.Sprint("case=testFositeSqlStoreTransactionCommitPKCERequest"), testFositeSqlStoreTransactionCommitPKCERequest(store))
t.Run(fmt.Sprint("case=testFositeSqlStoreTransactionRollbackPKCERequest"), testFositeSqlStoreTransactionRollbackPKCERequest(store))
t.Run(fmt.Sprint("case=testFositeSqlStoreTransactionCommitOpenIdConnectSession"), testFositeSqlStoreTransactionCommitOpenIdConnectSession(store))
t.Run(fmt.Sprint("case=testFositeSqlStoreTransactionRollbackOpenIdConnectSession"), testFositeSqlStoreTransactionRollbackOpenIdConnectSession(store))

}
t.Run(fmt.Sprintf("case=testHelperCreateGetDeleteAuthorizeCodes/db=%s", k), testHelperCreateGetDeleteAuthorizeCodes(store))
t.Run(fmt.Sprintf("case=testHelperCreateGetDeleteAccessTokenSession/db=%s", k), testHelperCreateGetDeleteAccessTokenSession(store))
@@ -385,3 +398,228 @@ func testHelperFlushTokens(x ManagerTestSetup, lifespan time.Duration) func(t *t
require.Error(t, err)
}
}

func testFositeSqlStoreTransactionCommitAccessToken(m ManagerTestSetup) func(t *testing.T) {
return func(t *testing.T) {
{
doTestCommit(m, t, m.F.CreateAccessTokenSession, m.F.GetAccessTokenSession, m.F.RevokeAccessToken)
doTestCommit(m, t, m.F.CreateAccessTokenSession, m.F.GetAccessTokenSession, m.F.DeleteAccessTokenSession)
}
}
}

func testFositeSqlStoreTransactionRollbackAccessToken(m ManagerTestSetup) func(t *testing.T) {
return func(t *testing.T) {
{
doTestRollback(m, t, m.F.CreateAccessTokenSession, m.F.GetAccessTokenSession, m.F.RevokeAccessToken)
doTestRollback(m, t, m.F.CreateAccessTokenSession, m.F.GetAccessTokenSession, m.F.DeleteAccessTokenSession)
}
}
}

func testFositeSqlStoreTransactionCommitRefreshToken(m ManagerTestSetup) func(t *testing.T) {

return func(t *testing.T) {
doTestCommit(m, t, m.F.CreateRefreshTokenSession, m.F.GetRefreshTokenSession, m.F.RevokeRefreshToken)
doTestCommit(m, t, m.F.CreateRefreshTokenSession, m.F.GetRefreshTokenSession, m.F.DeleteRefreshTokenSession)
}
}

func testFositeSqlStoreTransactionRollbackRefreshToken(m ManagerTestSetup) func(t *testing.T) {
return func(t *testing.T) {
doTestRollback(m, t, m.F.CreateRefreshTokenSession, m.F.GetRefreshTokenSession, m.F.RevokeRefreshToken)
doTestRollback(m, t, m.F.CreateRefreshTokenSession, m.F.GetRefreshTokenSession, m.F.DeleteRefreshTokenSession)
}
}

func testFositeSqlStoreTransactionCommitAuthorizeCode(m ManagerTestSetup) func(t *testing.T) {

return func(t *testing.T) {
doTestCommit(m, t, m.F.CreateAuthorizeCodeSession, m.F.GetAuthorizeCodeSession, m.F.InvalidateAuthorizeCodeSession)
}
}

func testFositeSqlStoreTransactionRollbackAuthorizeCode(m ManagerTestSetup) func(t *testing.T) {
return func(t *testing.T) {
doTestRollback(m, t, m.F.CreateAuthorizeCodeSession, m.F.GetAuthorizeCodeSession, m.F.InvalidateAuthorizeCodeSession)
}
}

func testFositeSqlStoreTransactionCommitPKCERequest(m ManagerTestSetup) func(t *testing.T) {

return func(t *testing.T) {
doTestCommit(m, t, m.F.CreatePKCERequestSession, m.F.GetPKCERequestSession, m.F.DeletePKCERequestSession)
}
}

func testFositeSqlStoreTransactionRollbackPKCERequest(m ManagerTestSetup) func(t *testing.T) {
return func(t *testing.T) {
doTestRollback(m, t, m.F.CreatePKCERequestSession, m.F.GetPKCERequestSession, m.F.DeletePKCERequestSession)
}
}

// OpenIdConnect tests can't use the helper functions, due to the signature of GetOpenIdConnectSession being
// different from the other getter methods
func testFositeSqlStoreTransactionCommitOpenIdConnectSession(m ManagerTestSetup) func(t *testing.T) {
return func(t *testing.T) {
txnStore, ok := m.F.(storage.Transactional)
require.True(t, ok)
ctx := context.Background()
ctx, err := txnStore.BeginTX(ctx)
require.NoError(t, err)
signature := uuid.New()
testRequest := createTestRequest(signature)
err = m.F.CreateOpenIDConnectSession(ctx, signature, testRequest)
require.NoError(t, err)
err = txnStore.Commit(ctx)
require.NoError(t, err)

// Require a new context, since the old one contains the transaction.
res, err := m.F.GetOpenIDConnectSession(context.Background(), signature, testRequest)
// session should have been created successfully because Commit did not return an error
require.NoError(t, err)
AssertObjectKeysEqual(t, &defaultRequest, res, "RequestedScope", "GrantedScope", "Form", "Session")

// test delete within a transaction
ctx, err = txnStore.BeginTX(context.Background())
err = m.F.DeleteOpenIDConnectSession(ctx, signature)
require.NoError(t, err)
err = txnStore.Commit(ctx)
require.NoError(t, err)

// Require a new context, since the old one contains the transaction.
_, err = m.F.GetOpenIDConnectSession(context.Background(), signature, testRequest)
// Since commit worked for delete, we should get an error here.
require.Error(t, err)
}
}

func testFositeSqlStoreTransactionRollbackOpenIdConnectSession(m ManagerTestSetup) func(t *testing.T) {
return func(t *testing.T) {
txnStore, ok := m.F.(storage.Transactional)
require.True(t, ok)
ctx := context.Background()
ctx, err := txnStore.BeginTX(ctx)
require.NoError(t, err)

signature := uuid.New()
testRequest := createTestRequest(signature)
err = m.F.CreateOpenIDConnectSession(ctx, signature, testRequest)
require.NoError(t, err)
err = txnStore.Rollback(ctx)
require.NoError(t, err)

// Require a new context, since the old one contains the transaction.
ctx = context.Background()
_, err = m.F.GetOpenIDConnectSession(ctx, signature, testRequest)
// Since we rolled back above, the session should not exist and getting it should result in an error
require.Error(t, err)

// create a new session, delete it, then rollback the delete. We should be able to then get it.
signature2 := uuid.New()
testRequest2 := createTestRequest(signature2)
err = m.F.CreateOpenIDConnectSession(ctx, signature2, testRequest2)
require.NoError(t, err)
_, err = m.F.GetOpenIDConnectSession(ctx, signature2, testRequest2)
require.NoError(t, err)

ctx, err = txnStore.BeginTX(context.Background())
err = m.F.DeleteOpenIDConnectSession(ctx, signature2)
require.NoError(t, err)
err = txnStore.Rollback(ctx)

require.NoError(t, err)
_, err = m.F.GetOpenIDConnectSession(context.Background(), signature2, testRequest2)
require.NoError(t, err)
}
}

func doTestCommit(m ManagerTestSetup, t *testing.T,
createFn func(context.Context, string, fosite.Requester) error,
getFn func(context.Context, string, fosite.Session) (fosite.Requester, error),
revokeFn func(context.Context, string) error,
) {

txnStore, ok := m.F.(storage.Transactional)
require.True(t, ok)
ctx := context.Background()
ctx, err := txnStore.BeginTX(ctx)
require.NoError(t, err)
signature := uuid.New()
err = createFn(ctx, signature, createTestRequest(signature))
require.NoError(t, err)
err = txnStore.Commit(ctx)
require.NoError(t, err)

// Require a new context, since the old one contains the transaction.
res, err := getFn(context.Background(), signature, &Session{})
// token should have been created successfully because Commit did not return an error
require.NoError(t, err)
AssertObjectKeysEqual(t, &defaultRequest, res, "RequestedScope", "GrantedScope", "Form", "Session")

// testrevoke within a transaction
ctx, err = txnStore.BeginTX(context.Background())
err = revokeFn(ctx, signature)
require.NoError(t, err)
err = txnStore.Commit(ctx)
require.NoError(t, err)

// Require a new context, since the old one contains the transaction.
_, err = getFn(context.Background(), signature, &Session{})
// Since commit worked for revoke, we should get an error here.
require.Error(t, err)
}

func doTestRollback(m ManagerTestSetup, t *testing.T,
createFn func(context.Context, string, fosite.Requester) error,
getFn func(context.Context, string, fosite.Session) (fosite.Requester, error),
revokeFn func(context.Context, string) error,
) {
txnStore, ok := m.F.(storage.Transactional)
require.True(t, ok)

ctx := context.Background()
ctx, err := txnStore.BeginTX(ctx)
require.NoError(t, err)
signature := uuid.New()
err = createFn(ctx, signature, createTestRequest(signature))
require.NoError(t, err)
err = txnStore.Rollback(ctx)
require.NoError(t, err)

// Require a new context, since the old one contains the transaction.
ctx = context.Background()
_, err = getFn(ctx, signature, &Session{})
// Since we rolled back above, the token should not exist and getting it should result in an error
require.Error(t, err)

// create a new token, revoke it, then rollback the revoke. We should be able to then get it successfully.
signature2 := uuid.New()
err = createFn(ctx, signature2, createTestRequest(signature2))
require.NoError(t, err)
_, err = getFn(ctx, signature2, &Session{})
require.NoError(t, err)

ctx, err = txnStore.BeginTX(context.Background())
err = revokeFn(ctx, signature2)
require.NoError(t, err)
err = txnStore.Rollback(ctx)
require.NoError(t, err)

_, err = getFn(context.Background(), signature2, &Session{})
require.NoError(t, err)
}

func createTestRequest(id string) *fosite.Request {
return &fosite.Request{
ID: id,
RequestedAt: time.Now().UTC().Round(time.Second),
Client: &client.Client{ClientID: "foobar"},
RequestedScope: fosite.Arguments{"fa", "ba"},
GrantedScope: fosite.Arguments{"fa", "ba"},
RequestedAudience: fosite.Arguments{"ad1", "ad2"},
GrantedAudience: fosite.Arguments{"ad1", "ad2"},
Form: url.Values{"foo": []string{"bar", "baz"}},
Session: &Session{DefaultSession: &openid.DefaultSession{Subject: "bar"}},
}
}

0 comments on commit 65415ff

Please sign in to comment.
You can’t perform that action at this time.