feat: add oauth2 token exchange (#12196)

Co-authored-by: Steven Masley <stevenmasley@gmail.com>
This commit is contained in:
Asher
2024-02-20 14:58:43 -09:00
committed by GitHub
co-authored by Steven Masley
parent 07cccf9033
commit 4d39da294e
46 changed files with 4008 additions and 167 deletions
+83
View File
@@ -801,6 +801,25 @@ func (q *querier) DeleteOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID)
return q.db.DeleteOAuth2ProviderAppByID(ctx, id)
}
func (q *querier) DeleteOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) error {
code, err := q.db.GetOAuth2ProviderAppCodeByID(ctx, id)
if err != nil {
return err
}
if err := q.authorizeContext(ctx, rbac.ActionDelete, code); err != nil {
return err
}
return q.db.DeleteOAuth2ProviderAppCodeByID(ctx, id)
}
func (q *querier) DeleteOAuth2ProviderAppCodesByAppAndUserID(ctx context.Context, arg database.DeleteOAuth2ProviderAppCodesByAppAndUserIDParams) error {
if err := q.authorizeContext(ctx, rbac.ActionDelete,
rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(arg.UserID.String())); err != nil {
return err
}
return q.db.DeleteOAuth2ProviderAppCodesByAppAndUserID(ctx, arg)
}
func (q *querier) DeleteOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UUID) error {
if err := q.authorizeContext(ctx, rbac.ActionDelete, rbac.ResourceOAuth2ProviderAppSecret); err != nil {
return err
@@ -808,6 +827,14 @@ func (q *querier) DeleteOAuth2ProviderAppSecretByID(ctx context.Context, id uuid
return q.db.DeleteOAuth2ProviderAppSecretByID(ctx, id)
}
func (q *querier) DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx context.Context, arg database.DeleteOAuth2ProviderAppTokensByAppAndUserIDParams) error {
if err := q.authorizeContext(ctx, rbac.ActionDelete,
rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(arg.UserID.String())); err != nil {
return err
}
return q.db.DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx, arg)
}
func (q *querier) DeleteOldProvisionerDaemons(ctx context.Context) error {
if err := q.authorizeContext(ctx, rbac.ActionDelete, rbac.ResourceSystem); err != nil {
return err
@@ -1175,6 +1202,14 @@ func (q *querier) GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) (d
return q.db.GetOAuth2ProviderAppByID(ctx, id)
}
func (q *querier) GetOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderAppCode, error) {
return fetch(q.log, q.auth, q.db.GetOAuth2ProviderAppCodeByID)(ctx, id)
}
func (q *querier) GetOAuth2ProviderAppCodeByPrefix(ctx context.Context, secretPrefix []byte) (database.OAuth2ProviderAppCode, error) {
return fetch(q.log, q.auth, q.db.GetOAuth2ProviderAppCodeByPrefix)(ctx, secretPrefix)
}
func (q *querier) GetOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderAppSecret, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceOAuth2ProviderAppSecret); err != nil {
return database.OAuth2ProviderAppSecret{}, err
@@ -1182,6 +1217,10 @@ func (q *querier) GetOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UU
return q.db.GetOAuth2ProviderAppSecretByID(ctx, id)
}
func (q *querier) GetOAuth2ProviderAppSecretByPrefix(ctx context.Context, secretPrefix []byte) (database.OAuth2ProviderAppSecret, error) {
return fetch(q.log, q.auth, q.db.GetOAuth2ProviderAppSecretByPrefix)(ctx, secretPrefix)
}
func (q *querier) GetOAuth2ProviderAppSecretsByAppID(ctx context.Context, appID uuid.UUID) ([]database.OAuth2ProviderAppSecret, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceOAuth2ProviderAppSecret); err != nil {
return []database.OAuth2ProviderAppSecret{}, err
@@ -1189,6 +1228,22 @@ func (q *querier) GetOAuth2ProviderAppSecretsByAppID(ctx context.Context, appID
return q.db.GetOAuth2ProviderAppSecretsByAppID(ctx, appID)
}
func (q *querier) GetOAuth2ProviderAppTokenByPrefix(ctx context.Context, hashPrefix []byte) (database.OAuth2ProviderAppToken, error) {
token, err := q.db.GetOAuth2ProviderAppTokenByPrefix(ctx, hashPrefix)
if err != nil {
return database.OAuth2ProviderAppToken{}, err
}
// The user ID is on the API key so that has to be fetched.
key, err := q.db.GetAPIKeyByID(ctx, token.APIKeyID)
if err != nil {
return database.OAuth2ProviderAppToken{}, err
}
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(key.UserID.String())); err != nil {
return database.OAuth2ProviderAppToken{}, err
}
return token, nil
}
func (q *querier) GetOAuth2ProviderApps(ctx context.Context) ([]database.OAuth2ProviderApp, error) {
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceOAuth2ProviderApp); err != nil {
return []database.OAuth2ProviderApp{}, err
@@ -1196,6 +1251,15 @@ func (q *querier) GetOAuth2ProviderApps(ctx context.Context) ([]database.OAuth2P
return q.db.GetOAuth2ProviderApps(ctx)
}
func (q *querier) GetOAuth2ProviderAppsByUserID(ctx context.Context, userID uuid.UUID) ([]database.GetOAuth2ProviderAppsByUserIDRow, error) {
// This authz check is to make sure the caller can read all their own tokens.
if err := q.authorizeContext(ctx, rbac.ActionRead,
rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(userID.String())); err != nil {
return []database.GetOAuth2ProviderAppsByUserIDRow{}, err
}
return q.db.GetOAuth2ProviderAppsByUserID(ctx, userID)
}
func (q *querier) GetOAuthSigningKey(ctx context.Context) (string, error) {
if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceSystem); err != nil {
return "", err
@@ -2242,6 +2306,14 @@ func (q *querier) InsertOAuth2ProviderApp(ctx context.Context, arg database.Inse
return q.db.InsertOAuth2ProviderApp(ctx, arg)
}
func (q *querier) InsertOAuth2ProviderAppCode(ctx context.Context, arg database.InsertOAuth2ProviderAppCodeParams) (database.OAuth2ProviderAppCode, error) {
if err := q.authorizeContext(ctx, rbac.ActionCreate,
rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(arg.UserID.String())); err != nil {
return database.OAuth2ProviderAppCode{}, err
}
return q.db.InsertOAuth2ProviderAppCode(ctx, arg)
}
func (q *querier) InsertOAuth2ProviderAppSecret(ctx context.Context, arg database.InsertOAuth2ProviderAppSecretParams) (database.OAuth2ProviderAppSecret, error) {
if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceOAuth2ProviderAppSecret); err != nil {
return database.OAuth2ProviderAppSecret{}, err
@@ -2249,6 +2321,17 @@ func (q *querier) InsertOAuth2ProviderAppSecret(ctx context.Context, arg databas
return q.db.InsertOAuth2ProviderAppSecret(ctx, arg)
}
func (q *querier) InsertOAuth2ProviderAppToken(ctx context.Context, arg database.InsertOAuth2ProviderAppTokenParams) (database.OAuth2ProviderAppToken, error) {
key, err := q.db.GetAPIKeyByID(ctx, arg.APIKeyID)
if err != nil {
return database.OAuth2ProviderAppToken{}, err
}
if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(key.UserID.String())); err != nil {
return database.OAuth2ProviderAppToken{}, err
}
return q.db.InsertOAuth2ProviderAppToken(ctx, arg)
}
func (q *querier) InsertOrganization(ctx context.Context, arg database.InsertOrganizationParams) (database.Organization, error) {
return insert(q.log, q.auth, rbac.ResourceOrganization, q.db.InsertOrganization)(ctx, arg)
}
+139
View File
@@ -2316,6 +2316,34 @@ func (s *MethodTestSuite) TestOAuth2ProviderApps() {
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
check.Args(app.ID).Asserts(rbac.ResourceOAuth2ProviderApp, rbac.ActionRead).Returns(app)
}))
s.Run("GetOAuth2ProviderAppsByUserID", s.Subtest(func(db database.Store, check *expects) {
user := dbgen.User(s.T(), db, database.User{})
key, _ := dbgen.APIKey(s.T(), db, database.APIKey{
UserID: user.ID,
})
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
_ = dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
secret := dbgen.OAuth2ProviderAppSecret(s.T(), db, database.OAuth2ProviderAppSecret{
AppID: app.ID,
})
for i := 0; i < 5; i++ {
_ = dbgen.OAuth2ProviderAppToken(s.T(), db, database.OAuth2ProviderAppToken{
AppSecretID: secret.ID,
APIKeyID: key.ID,
})
}
check.Args(user.ID).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionRead).Returns([]database.GetOAuth2ProviderAppsByUserIDRow{
{
OAuth2ProviderApp: database.OAuth2ProviderApp{
ID: app.ID,
CallbackURL: app.CallbackURL,
Icon: app.Icon,
Name: app.Name,
},
TokenCount: 5,
},
})
}))
s.Run("InsertOAuth2ProviderApp", s.Subtest(func(db database.Store, check *expects) {
check.Args(database.InsertOAuth2ProviderAppParams{}).Asserts(rbac.ResourceOAuth2ProviderApp, rbac.ActionCreate)
}))
@@ -2361,6 +2389,13 @@ func (s *MethodTestSuite) TestOAuth2ProviderAppSecrets() {
})
check.Args(secret.ID).Asserts(rbac.ResourceOAuth2ProviderAppSecret, rbac.ActionRead).Returns(secret)
}))
s.Run("GetOAuth2ProviderAppSecretByPrefix", s.Subtest(func(db database.Store, check *expects) {
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
secret := dbgen.OAuth2ProviderAppSecret(s.T(), db, database.OAuth2ProviderAppSecret{
AppID: app.ID,
})
check.Args(secret.SecretPrefix).Asserts(rbac.ResourceOAuth2ProviderAppSecret, rbac.ActionRead).Returns(secret)
}))
s.Run("InsertOAuth2ProviderAppSecret", s.Subtest(func(db database.Store, check *expects) {
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
check.Args(database.InsertOAuth2ProviderAppSecretParams{
@@ -2386,3 +2421,107 @@ func (s *MethodTestSuite) TestOAuth2ProviderAppSecrets() {
check.Args(secret.ID).Asserts(rbac.ResourceOAuth2ProviderAppSecret, rbac.ActionDelete)
}))
}
func (s *MethodTestSuite) TestOAuth2ProviderAppCodes() {
s.Run("GetOAuth2ProviderAppCodeByID", s.Subtest(func(db database.Store, check *expects) {
user := dbgen.User(s.T(), db, database.User{})
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
code := dbgen.OAuth2ProviderAppCode(s.T(), db, database.OAuth2ProviderAppCode{
AppID: app.ID,
UserID: user.ID,
})
check.Args(code.ID).Asserts(code, rbac.ActionRead).Returns(code)
}))
s.Run("GetOAuth2ProviderAppCodeByPrefix", s.Subtest(func(db database.Store, check *expects) {
user := dbgen.User(s.T(), db, database.User{})
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
code := dbgen.OAuth2ProviderAppCode(s.T(), db, database.OAuth2ProviderAppCode{
AppID: app.ID,
UserID: user.ID,
})
check.Args(code.SecretPrefix).Asserts(code, rbac.ActionRead).Returns(code)
}))
s.Run("InsertOAuth2ProviderAppCode", s.Subtest(func(db database.Store, check *expects) {
user := dbgen.User(s.T(), db, database.User{})
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
check.Args(database.InsertOAuth2ProviderAppCodeParams{
AppID: app.ID,
UserID: user.ID,
}).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionCreate)
}))
s.Run("DeleteOAuth2ProviderAppCodeByID", s.Subtest(func(db database.Store, check *expects) {
user := dbgen.User(s.T(), db, database.User{})
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
code := dbgen.OAuth2ProviderAppCode(s.T(), db, database.OAuth2ProviderAppCode{
AppID: app.ID,
UserID: user.ID,
})
check.Args(code.ID).Asserts(code, rbac.ActionDelete)
}))
s.Run("DeleteOAuth2ProviderAppCodesByAppAndUserID", s.Subtest(func(db database.Store, check *expects) {
user := dbgen.User(s.T(), db, database.User{})
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
for i := 0; i < 5; i++ {
_ = dbgen.OAuth2ProviderAppCode(s.T(), db, database.OAuth2ProviderAppCode{
AppID: app.ID,
UserID: user.ID,
})
}
check.Args(database.DeleteOAuth2ProviderAppCodesByAppAndUserIDParams{
AppID: app.ID,
UserID: user.ID,
}).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionDelete)
}))
}
func (s *MethodTestSuite) TestOAuth2ProviderAppTokens() {
s.Run("InsertOAuth2ProviderAppToken", s.Subtest(func(db database.Store, check *expects) {
user := dbgen.User(s.T(), db, database.User{})
key, _ := dbgen.APIKey(s.T(), db, database.APIKey{
UserID: user.ID,
})
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
secret := dbgen.OAuth2ProviderAppSecret(s.T(), db, database.OAuth2ProviderAppSecret{
AppID: app.ID,
})
check.Args(database.InsertOAuth2ProviderAppTokenParams{
AppSecretID: secret.ID,
APIKeyID: key.ID,
}).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionCreate)
}))
s.Run("GetOAuth2ProviderAppTokenByPrefix", s.Subtest(func(db database.Store, check *expects) {
user := dbgen.User(s.T(), db, database.User{})
key, _ := dbgen.APIKey(s.T(), db, database.APIKey{
UserID: user.ID,
})
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
secret := dbgen.OAuth2ProviderAppSecret(s.T(), db, database.OAuth2ProviderAppSecret{
AppID: app.ID,
})
token := dbgen.OAuth2ProviderAppToken(s.T(), db, database.OAuth2ProviderAppToken{
AppSecretID: secret.ID,
APIKeyID: key.ID,
})
check.Args(token.HashPrefix).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionRead)
}))
s.Run("DeleteOAuth2ProviderAppTokensByAppAndUserID", s.Subtest(func(db database.Store, check *expects) {
user := dbgen.User(s.T(), db, database.User{})
key, _ := dbgen.APIKey(s.T(), db, database.APIKey{
UserID: user.ID,
})
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
secret := dbgen.OAuth2ProviderAppSecret(s.T(), db, database.OAuth2ProviderAppSecret{
AppID: app.ID,
})
for i := 0; i < 5; i++ {
_ = dbgen.OAuth2ProviderAppToken(s.T(), db, database.OAuth2ProviderAppToken{
AppSecretID: secret.ID,
APIKeyID: key.ID,
})
}
check.Args(database.DeleteOAuth2ProviderAppTokensByAppAndUserIDParams{
AppID: app.ID,
UserID: user.ID,
}).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionDelete)
}))
}