zitadel/internal/query/oidc_client_test.go
Livio Spring d01bd1c51a
fix: correctly check app state on authentication (#8630)
# Which Problems Are Solved

In Zitadel, even after an organization is deactivated, associated
projects, respectively their applications remain active. Users across
other organizations can still log in and access through these
applications, leading to unauthorized access.
Additionally, if a project was deactivated access to applications was
also still possible.

# How the Problems Are Solved

- Correctly check the status of the organization and related project. 
(Corresponding functions have been renamed to `Active...`)
2024-09-17 11:34:14 +00:00

242 lines
9.3 KiB
Go

package query
import (
"database/sql"
"database/sql/driver"
_ "embed"
"regexp"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/zerrors"
)
var (
//go:embed testdata/oidc_client_jwt.json
testdataOidcClientJWT string
//go:embed testdata/oidc_client_public.json
testdataOidcClientPublic string
//go:embed testdata/oidc_client_public_old_id.json
testdataOidcClientPublicOldId string
//go:embed testdata/oidc_client_secret.json
testdataOidcClientSecret string
//go:embed testdata/oidc_client_no_settings.json
testdataOidcClientNoSettings string
)
func TestQueries_ActiveOIDCClientByID(t *testing.T) {
expQuery := regexp.QuoteMeta(oidcClientQuery)
cols := []string{"client"}
pubkey := `-----BEGIN RSA PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2ufAL1b72bIy1ar+Ws6b
GohJJQFB7dfRapDqeqM8Ukp6CVdPzq/pOz1viAq50yzWZJryF+2wshFAKGF9A2/B
2Yf9bJXPZ/KbkFrYT3NTvYDkvlaSTl9mMnzrU29s48F1PTWKfB+C3aMsOEG1BufV
s63qF4nrEPjSbhljIco9FZq4XppIzhMQ0fDdA/+XygCJqvuaL0LibM1KrlUdnu71
YekhSJjEPnvOisXIk4IXywoGIOwtjxkDvNItQvaMVldr4/kb6uvbgdWwq5EwBZXq
low2kyJov38V4Uk2I8kuXpLcnrpw5Tio2ooiUE27b0vHZqBKOei9Uo88qCrn3EKx
6QIDAQAB
-----END RSA PUBLIC KEY-----
`
tests := []struct {
name string
mock sqlExpectation
want *OIDCClient
wantErr error
}{
{
name: "no rows",
mock: mockQueryErr(expQuery, sql.ErrNoRows, "instanceID", "clientID", true),
wantErr: zerrors.ThrowNotFound(sql.ErrNoRows, "QUERY-wu6Ee", "Errors.App.NotFound"),
},
{
name: "internal error",
mock: mockQueryErr(expQuery, sql.ErrConnDone, "instanceID", "clientID", true),
wantErr: zerrors.ThrowInternal(sql.ErrConnDone, "QUERY-ieR7R", "Errors.Internal"),
},
{
name: "jwt client",
mock: mockQuery(expQuery, cols, []driver.Value{testdataOidcClientJWT}, "instanceID", "clientID", true),
want: &OIDCClient{
InstanceID: "230690539048009730",
AppID: "236647088211886082",
State: domain.AppStateActive,
ClientID: "236647088211951618",
HashedSecret: "",
RedirectURIs: []string{"http://localhost:9999/auth/callback"},
ResponseTypes: []domain.OIDCResponseType{domain.OIDCResponseTypeCode},
GrantTypes: []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode, domain.OIDCGrantTypeRefreshToken},
ApplicationType: domain.OIDCApplicationTypeWeb,
AuthMethodType: domain.OIDCAuthMethodTypePrivateKeyJWT,
PostLogoutRedirectURIs: []string{"https://example.com/logout"},
IsDevMode: true,
AccessTokenType: domain.OIDCTokenTypeJWT,
AccessTokenRoleAssertion: true,
IDTokenRoleAssertion: true,
IDTokenUserinfoAssertion: true,
ClockSkew: 1000000000,
AdditionalOrigins: []string{"https://example.com"},
ProjectID: "236645808328409090",
ProjectRoleAssertion: true,
PublicKeys: map[string][]byte{"236647201860747266": []byte(pubkey)},
ProjectRoleKeys: []string{"role1", "role2"},
Settings: &OIDCSettings{
AccessTokenLifetime: 43200000000000,
IdTokenLifetime: 43200000000000,
},
},
},
{
name: "public client",
mock: mockQuery(expQuery, cols, []driver.Value{testdataOidcClientPublic}, "instanceID", "clientID", true),
want: &OIDCClient{
InstanceID: "230690539048009730",
AppID: "236646457053020162",
State: domain.AppStateActive,
ClientID: "236646457053085698",
HashedSecret: "",
RedirectURIs: []string{"http://localhost:9999/auth/callback"},
ResponseTypes: []domain.OIDCResponseType{domain.OIDCResponseTypeCode},
GrantTypes: []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
ApplicationType: domain.OIDCApplicationTypeWeb,
AuthMethodType: domain.OIDCAuthMethodTypeNone,
PostLogoutRedirectURIs: nil,
IsDevMode: true,
AccessTokenType: domain.OIDCTokenTypeBearer,
AccessTokenRoleAssertion: false,
IDTokenRoleAssertion: false,
IDTokenUserinfoAssertion: false,
ClockSkew: 0,
AdditionalOrigins: nil,
PublicKeys: nil,
ProjectID: "236645808328409090",
ProjectRoleAssertion: true,
ProjectRoleKeys: []string{"role1", "role2"},
Settings: &OIDCSettings{
AccessTokenLifetime: 43200000000000,
IdTokenLifetime: 43200000000000,
},
},
},
{
name: "public client",
mock: mockQuery(expQuery, cols, []driver.Value{testdataOidcClientPublicOldId}, "instanceID", "clientID", true),
want: &OIDCClient{
InstanceID: "230690539048009730",
AppID: "236646457053020162",
State: domain.AppStateActive,
ClientID: "236646457053085698@tests",
HashedSecret: "",
RedirectURIs: []string{"http://localhost:9999/auth/callback"},
ResponseTypes: []domain.OIDCResponseType{domain.OIDCResponseTypeCode},
GrantTypes: []domain.OIDCGrantType{domain.OIDCGrantTypeAuthorizationCode},
ApplicationType: domain.OIDCApplicationTypeWeb,
AuthMethodType: domain.OIDCAuthMethodTypeNone,
PostLogoutRedirectURIs: nil,
IsDevMode: true,
AccessTokenType: domain.OIDCTokenTypeBearer,
AccessTokenRoleAssertion: false,
IDTokenRoleAssertion: false,
IDTokenUserinfoAssertion: false,
ClockSkew: 0,
AdditionalOrigins: nil,
PublicKeys: nil,
ProjectID: "236645808328409090",
ProjectRoleAssertion: true,
ProjectRoleKeys: []string{"role1", "role2"},
Settings: &OIDCSettings{
AccessTokenLifetime: 43200000000000,
IdTokenLifetime: 43200000000000,
},
},
},
{
name: "secret client",
mock: mockQuery(expQuery, cols, []driver.Value{testdataOidcClientSecret}, "instanceID", "clientID", true),
want: &OIDCClient{
InstanceID: "230690539048009730",
AppID: "236646858984783874",
State: domain.AppStateActive,
ClientID: "236646858984849410",
HashedSecret: "$2a$14$OzZ0XEZZEtD13py/EPba2evsS6WcKZ5orVMj9pWHEGEHmLu2h3PFq",
RedirectURIs: []string{"http://localhost:9999/auth/callback"},
ResponseTypes: []domain.OIDCResponseType{0},
GrantTypes: []domain.OIDCGrantType{0},
ApplicationType: domain.OIDCApplicationTypeWeb,
AuthMethodType: domain.OIDCAuthMethodTypeBasic,
PostLogoutRedirectURIs: nil,
IsDevMode: true,
AccessTokenType: domain.OIDCTokenTypeBearer,
AccessTokenRoleAssertion: false,
IDTokenRoleAssertion: false,
IDTokenUserinfoAssertion: false,
ClockSkew: 0,
AdditionalOrigins: nil,
PublicKeys: nil,
ProjectID: "236645808328409090",
ProjectRoleAssertion: false,
ProjectRoleKeys: []string{"role1", "role2"},
Settings: &OIDCSettings{
AccessTokenLifetime: 43200000000000,
IdTokenLifetime: 43200000000000,
},
},
},
{
name: "no oidc settings",
mock: mockQuery(expQuery, cols, []driver.Value{testdataOidcClientNoSettings}, "instanceID", "clientID", true),
want: &OIDCClient{
InstanceID: "239520764275982338",
AppID: "239520764276441090",
State: domain.AppStateActive,
ClientID: "239520764779364354",
HashedSecret: "",
RedirectURIs: []string{
"http://test2-qucuh5.localhost:9000/ui/console/auth/callback",
"http://test.localhost.com:9000/ui/console/auth/callback"},
ResponseTypes: []domain.OIDCResponseType{0},
GrantTypes: []domain.OIDCGrantType{0},
ApplicationType: domain.OIDCApplicationTypeUserAgent,
AuthMethodType: domain.OIDCAuthMethodTypeNone,
PostLogoutRedirectURIs: []string{
"http://test2-qucuh5.localhost:9000/ui/console/signedout",
"http://test.localhost.com:9000/ui/console/signedout",
},
IsDevMode: true,
AccessTokenType: domain.OIDCTokenTypeBearer,
AccessTokenRoleAssertion: false,
IDTokenRoleAssertion: false,
IDTokenUserinfoAssertion: false,
ClockSkew: 0,
AdditionalOrigins: nil,
PublicKeys: nil,
ProjectID: "239520764276178946",
ProjectRoleAssertion: false,
ProjectRoleKeys: nil,
Settings: nil,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
execMock(t, tt.mock, func(db *sql.DB) {
q := &Queries{
client: &database.DB{
DB: db,
Database: &prepareDB{},
},
}
ctx := authz.NewMockContext("instanceID", "orgID", "loginClient")
got, err := q.ActiveOIDCClientByID(ctx, "clientID", true)
require.ErrorIs(t, err, tt.wantErr)
assert.Equal(t, tt.want, got)
})
})
}
}