mirror of https://github.com/ory/hydra
321 lines
12 KiB
Go
321 lines
12 KiB
Go
// Copyright © 2022 Ory Corp
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package trust_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/go-jose/go-jose/v3"
|
|
"github.com/gofrs/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/suite"
|
|
"github.com/tidwall/gjson"
|
|
|
|
hydra "github.com/ory/hydra-client-go/v2"
|
|
"github.com/ory/hydra/v2/driver"
|
|
"github.com/ory/hydra/v2/driver/config"
|
|
"github.com/ory/hydra/v2/internal/testhelpers"
|
|
"github.com/ory/hydra/v2/jwk"
|
|
"github.com/ory/hydra/v2/oauth2/trust"
|
|
"github.com/ory/hydra/v2/x"
|
|
"github.com/ory/x/contextx"
|
|
"github.com/ory/x/pointerx"
|
|
)
|
|
|
|
// Define the suite, and absorb the built-in basic suite
|
|
// functionality from testify - including a T() method which
|
|
// returns the current testing context.
|
|
type HandlerTestSuite struct {
|
|
suite.Suite
|
|
registry driver.Registry
|
|
server *httptest.Server
|
|
hydraClient *hydra.APIClient
|
|
publicKey *rsa.PublicKey
|
|
}
|
|
|
|
// Setup will run before the tests in the suite are run.
|
|
func (s *HandlerTestSuite) SetupTest() {
|
|
conf := testhelpers.NewConfigurationWithDefaults()
|
|
conf.MustSet(context.Background(), config.KeySubjectTypesSupported, []string{"public"})
|
|
conf.MustSet(context.Background(), config.KeyDefaultClientScope, []string{"foo", "bar"})
|
|
s.registry = testhelpers.NewRegistryMemory(s.T(), conf, &contextx.Default{})
|
|
|
|
router := x.NewRouterAdmin(conf.AdminURL)
|
|
handler := trust.NewHandler(s.registry)
|
|
handler.SetRoutes(router)
|
|
jwkHandler := jwk.NewHandler(s.registry)
|
|
jwkHandler.SetRoutes(router, x.NewRouterPublic(), func(h http.Handler) http.Handler {
|
|
return h
|
|
})
|
|
s.server = httptest.NewServer(router)
|
|
|
|
c := hydra.NewAPIClient(hydra.NewConfiguration())
|
|
c.GetConfig().Servers = hydra.ServerConfigurations{{URL: s.server.URL}}
|
|
s.hydraClient = c
|
|
s.publicKey = s.generatePublicKey()
|
|
}
|
|
|
|
// Setup before each test.
|
|
func (s *HandlerTestSuite) SetupSuite() {}
|
|
|
|
// Will run after all the tests in the suite have been run.
|
|
func (s *HandlerTestSuite) TearDownSuite() {}
|
|
|
|
// Will run after each test in the suite.
|
|
func (s *HandlerTestSuite) TearDownTest() {}
|
|
|
|
// In order for 'go test' to run this suite, we need to create
|
|
// a normal test function and pass our suite to suite.Run.
|
|
func TestHandlerTestSuite(t *testing.T) {
|
|
suite.Run(t, new(HandlerTestSuite))
|
|
}
|
|
|
|
func (s *HandlerTestSuite) TestGrantCanBeCreatedAndFetched() {
|
|
createRequestParams := s.newCreateJwtBearerGrantParams(
|
|
"ory",
|
|
"hackerman@example.com",
|
|
false,
|
|
[]string{"openid", "offline", "profile"},
|
|
time.Now().Add(time.Hour),
|
|
)
|
|
model := createRequestParams
|
|
|
|
ctx := context.Background()
|
|
createResult, _, err := s.hydraClient.OAuth2API.TrustOAuth2JwtGrantIssuer(ctx).TrustOAuth2JwtGrantIssuer(createRequestParams).Execute()
|
|
s.Require().NoError(err, "no errors expected on grant creation")
|
|
s.NotEmpty(createResult.Id, " grant id expected to be non-empty")
|
|
s.Equal(model.Issuer, *createResult.Issuer, "issuer must match")
|
|
s.Equal(*model.Subject, *createResult.Subject, "subject must match")
|
|
s.Equal(model.Scope, createResult.Scope, "scopes must match")
|
|
s.Equal(model.Issuer, *createResult.PublicKey.Set, "public key set must match grant issuer")
|
|
s.Equal(model.Jwk.Kid, *createResult.PublicKey.Kid, "public key id must match")
|
|
s.Equal(model.ExpiresAt.Round(time.Second).UTC().String(), createResult.ExpiresAt.Round(time.Second).UTC().String(), "expiration date must match")
|
|
|
|
getResult, _, err := s.hydraClient.OAuth2API.GetTrustedOAuth2JwtGrantIssuer(ctx, *createResult.Id).Execute()
|
|
s.Require().NoError(err, "no errors expected on grant fetching")
|
|
s.Equal(*createResult.Id, *getResult.Id, " grant id must match")
|
|
s.Equal(model.Issuer, *getResult.Issuer, "issuer must match")
|
|
s.Equal(*model.Subject, *getResult.Subject, "subject must match")
|
|
s.Equal(model.Scope, getResult.Scope, "scopes must match")
|
|
s.Equal(model.Issuer, *getResult.PublicKey.Set, "public key set must match grant issuer")
|
|
s.Equal(model.Jwk.Kid, *getResult.PublicKey.Kid, "public key id must match")
|
|
s.Equal(model.ExpiresAt.Round(time.Second).UTC().String(), getResult.ExpiresAt.Round(time.Second).UTC().String(), "expiration date must match")
|
|
}
|
|
|
|
func (s *HandlerTestSuite) TestGrantCanNotBeCreatedWithSameIssuerSubjectKey() {
|
|
createRequestParams := s.newCreateJwtBearerGrantParams(
|
|
"ory",
|
|
"hackerman@example.com",
|
|
false,
|
|
[]string{"openid", "offline", "profile"},
|
|
time.Now().Add(time.Hour),
|
|
)
|
|
|
|
ctx := context.Background()
|
|
_, _, err := s.hydraClient.OAuth2API.TrustOAuth2JwtGrantIssuer(ctx).TrustOAuth2JwtGrantIssuer(createRequestParams).Execute()
|
|
s.Require().NoError(err, "no errors expected on grant creation")
|
|
|
|
_, _, err = s.hydraClient.OAuth2API.TrustOAuth2JwtGrantIssuer(ctx).TrustOAuth2JwtGrantIssuer(createRequestParams).Execute()
|
|
s.Require().Error(err, "expected error, because grant with same issuer+subject+kid exists")
|
|
|
|
createRequestParams.Jwk.Kid = uuid.Must(uuid.NewV4()).String()
|
|
_, _, err = s.hydraClient.OAuth2API.TrustOAuth2JwtGrantIssuer(ctx).TrustOAuth2JwtGrantIssuer(createRequestParams).Execute()
|
|
s.NoError(err, "no errors expected on grant creation, because kid is now different")
|
|
}
|
|
|
|
func (s *HandlerTestSuite) TestGrantCanNotBeCreatedWithSubjectAndAnySubject() {
|
|
createRequestParams := s.newCreateJwtBearerGrantParams(
|
|
"ory",
|
|
"hackerman@example.com",
|
|
true,
|
|
[]string{"openid", "offline", "profile"},
|
|
time.Now().Add(time.Hour),
|
|
)
|
|
|
|
_, _, err := s.hydraClient.OAuth2API.TrustOAuth2JwtGrantIssuer(context.Background()).TrustOAuth2JwtGrantIssuer(createRequestParams).Execute()
|
|
s.Require().Error(err, "expected error, because a grant with a subject and allow_any_subject cannot be created")
|
|
}
|
|
|
|
func (s *HandlerTestSuite) TestGrantCanNotBeCreatedWithUnknownJWK() {
|
|
createRequestParams := hydra.TrustOAuth2JwtGrantIssuer{
|
|
AllowAnySubject: pointerx.Ptr(true),
|
|
ExpiresAt: time.Now().Add(1 * time.Hour),
|
|
Issuer: "ory",
|
|
Jwk: hydra.JsonWebKey{
|
|
Alg: "unknown",
|
|
},
|
|
Scope: []string{"openid", "offline", "profile"},
|
|
}
|
|
|
|
_, res, err := s.hydraClient.OAuth2API.TrustOAuth2JwtGrantIssuer(context.Background()).TrustOAuth2JwtGrantIssuer(createRequestParams).Execute()
|
|
s.Require().Error(err, "expected error, because the key type was unknown")
|
|
s.Assert().Equal(http.StatusBadRequest, res.StatusCode)
|
|
body, _ := io.ReadAll(res.Body)
|
|
s.Contains(gjson.GetBytes(body, "error_description").String(), "unknown json web key type")
|
|
}
|
|
|
|
func (s *HandlerTestSuite) TestGrantCanNotBeCreatedWithMissingFields() {
|
|
createRequestParams := s.newCreateJwtBearerGrantParams(
|
|
"",
|
|
"hackerman@example.com",
|
|
false,
|
|
[]string{"openid", "offline", "profile"},
|
|
time.Now().Add(time.Hour),
|
|
)
|
|
|
|
_, _, err := s.hydraClient.OAuth2API.TrustOAuth2JwtGrantIssuer(context.Background()).TrustOAuth2JwtGrantIssuer(createRequestParams).Execute()
|
|
s.Require().Error(err, "expected error, because grant missing issuer")
|
|
|
|
createRequestParams = s.newCreateJwtBearerGrantParams(
|
|
"ory",
|
|
"",
|
|
false,
|
|
[]string{"openid", "offline", "profile"},
|
|
time.Now().Add(time.Hour),
|
|
)
|
|
|
|
_, _, err = s.hydraClient.OAuth2API.TrustOAuth2JwtGrantIssuer(context.Background()).TrustOAuth2JwtGrantIssuer(createRequestParams).Execute()
|
|
s.Require().Error(err, "expected error, because grant missing subject")
|
|
|
|
createRequestParams = s.newCreateJwtBearerGrantParams(
|
|
"ory",
|
|
"hackerman@example.com",
|
|
false,
|
|
[]string{"openid", "offline", "profile"},
|
|
time.Time{},
|
|
)
|
|
|
|
_, _, err = s.hydraClient.OAuth2API.TrustOAuth2JwtGrantIssuer(context.Background()).TrustOAuth2JwtGrantIssuer(createRequestParams).Execute()
|
|
s.Error(err, "expected error, because grant missing expiration date")
|
|
}
|
|
|
|
func (s *HandlerTestSuite) TestGrantPublicCanBeFetched() {
|
|
createRequestParams := s.newCreateJwtBearerGrantParams(
|
|
"ory",
|
|
"hackerman@example.com",
|
|
false,
|
|
[]string{"openid", "offline", "profile"},
|
|
time.Now().Add(time.Hour),
|
|
)
|
|
|
|
_, _, err := s.hydraClient.OAuth2API.TrustOAuth2JwtGrantIssuer(context.Background()).TrustOAuth2JwtGrantIssuer(createRequestParams).Execute()
|
|
s.Require().NoError(err, "no error expected on grant creation")
|
|
|
|
getResult, _, err := s.hydraClient.JwkAPI.GetJsonWebKey(context.Background(), createRequestParams.Issuer, createRequestParams.Jwk.Kid).Execute()
|
|
|
|
s.Require().NoError(err, "no error expected on fetching public key")
|
|
s.Equal(createRequestParams.Jwk.Kid, getResult.Keys[0].Kid)
|
|
}
|
|
|
|
func (s *HandlerTestSuite) TestGrantWithAnySubjectCanBeCreated() {
|
|
createRequestParams := s.newCreateJwtBearerGrantParams(
|
|
"ory",
|
|
"",
|
|
true,
|
|
[]string{"openid", "offline", "profile"},
|
|
time.Now().Add(time.Hour),
|
|
)
|
|
|
|
grant, _, err := s.hydraClient.OAuth2API.TrustOAuth2JwtGrantIssuer(context.Background()).TrustOAuth2JwtGrantIssuer(createRequestParams).Execute()
|
|
s.Require().NoError(err, "no error expected on grant creation")
|
|
|
|
assert.Empty(s.T(), grant.Subject)
|
|
assert.Truef(s.T(), *grant.AllowAnySubject, "grant with any subject must be true")
|
|
}
|
|
|
|
func (s *HandlerTestSuite) TestGrantListCanBeFetched() {
|
|
createRequestParams := s.newCreateJwtBearerGrantParams(
|
|
"ory",
|
|
"hackerman@example.com",
|
|
false,
|
|
[]string{"openid", "offline", "profile"},
|
|
time.Now().Add(time.Hour),
|
|
)
|
|
createRequestParams2 := s.newCreateJwtBearerGrantParams(
|
|
"ory2",
|
|
"safetyman@example.com",
|
|
false,
|
|
[]string{"profile"},
|
|
time.Now().Add(time.Hour),
|
|
)
|
|
|
|
_, _, err := s.hydraClient.OAuth2API.TrustOAuth2JwtGrantIssuer(context.Background()).TrustOAuth2JwtGrantIssuer(createRequestParams).Execute()
|
|
s.Require().NoError(err, "no errors expected on grant creation")
|
|
|
|
_, _, err = s.hydraClient.OAuth2API.TrustOAuth2JwtGrantIssuer(context.Background()).TrustOAuth2JwtGrantIssuer(createRequestParams2).Execute()
|
|
s.Require().NoError(err, "no errors expected on grant creation")
|
|
|
|
getResult, _, err := s.hydraClient.OAuth2API.ListTrustedOAuth2JwtGrantIssuers(context.Background()).Execute()
|
|
s.Require().NoError(err, "no errors expected on grant list fetching")
|
|
s.Require().Len(getResult, 2, "expected to get list of 2 grants")
|
|
s.ElementsMatch([]string{createRequestParams.Issuer, createRequestParams2.Issuer}, []string{*getResult[0].Issuer, *getResult[1].Issuer})
|
|
|
|
getResult, _, err = s.hydraClient.OAuth2API.ListTrustedOAuth2JwtGrantIssuers(context.Background()).Issuer(createRequestParams2.Issuer).Execute()
|
|
|
|
s.Require().NoError(err, "no errors expected on grant list fetching")
|
|
s.Require().Len(getResult, 1, "expected to get list of 1 grant, when filtering by issuer")
|
|
s.Equal(createRequestParams2.Issuer, *getResult[0].Issuer, "issuer must match")
|
|
}
|
|
|
|
func (s *HandlerTestSuite) TestGrantCanBeDeleted() {
|
|
createRequestParams := s.newCreateJwtBearerGrantParams(
|
|
"ory",
|
|
"hackerman@example.com",
|
|
false,
|
|
[]string{"openid", "offline", "profile"},
|
|
time.Now().Add(time.Hour),
|
|
)
|
|
|
|
createResult, _, err := s.hydraClient.OAuth2API.TrustOAuth2JwtGrantIssuer(context.Background()).TrustOAuth2JwtGrantIssuer(createRequestParams).Execute()
|
|
s.Require().NoError(err, "no errors expected on grant creation")
|
|
|
|
_, err = s.hydraClient.OAuth2API.DeleteTrustedOAuth2JwtGrantIssuer(context.Background(), *createResult.Id).Execute()
|
|
s.Require().NoError(err, "no errors expected on grant deletion")
|
|
|
|
_, err = s.hydraClient.OAuth2API.DeleteTrustedOAuth2JwtGrantIssuer(context.Background(), *createResult.Id).Execute()
|
|
s.Error(err, "expected error, because grant has been already deleted")
|
|
}
|
|
|
|
func (s *HandlerTestSuite) generateJWK(publicKey *rsa.PublicKey) hydra.JsonWebKey {
|
|
var b bytes.Buffer
|
|
s.Require().NoError(json.NewEncoder(&b).Encode(&jose.JSONWebKey{
|
|
Key: publicKey,
|
|
KeyID: uuid.Must(uuid.NewV4()).String(),
|
|
Algorithm: string(jose.RS256),
|
|
Use: "sig",
|
|
}))
|
|
|
|
var mJWK hydra.JsonWebKey
|
|
s.Require().NoError(json.NewDecoder(&b).Decode(&mJWK))
|
|
return mJWK
|
|
}
|
|
|
|
func (s *HandlerTestSuite) newCreateJwtBearerGrantParams(
|
|
issuer, subject string, allowAnySubject bool, scope []string, expiresAt time.Time,
|
|
) hydra.TrustOAuth2JwtGrantIssuer {
|
|
return hydra.TrustOAuth2JwtGrantIssuer{
|
|
ExpiresAt: expiresAt,
|
|
Issuer: issuer,
|
|
Jwk: s.generateJWK(s.publicKey),
|
|
Scope: scope,
|
|
Subject: pointerx.Ptr(subject),
|
|
AllowAnySubject: pointerx.Ptr(allowAnySubject),
|
|
}
|
|
}
|
|
|
|
func (s *HandlerTestSuite) generatePublicKey() *rsa.PublicKey {
|
|
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
s.Require().NoError(err)
|
|
return &privateKey.PublicKey
|
|
}
|