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...`)
This commit is contained in:
Livio Spring
2024-09-17 13:34:14 +02:00
committed by GitHub
parent 77aa02a521
commit d01bd1c51a
13 changed files with 299 additions and 146 deletions

View File

@@ -1,6 +1,7 @@
package query
import (
"context"
"database/sql"
"database/sql/driver"
"errors"
@@ -9,13 +10,15 @@ import (
"testing"
"time"
sq "github.com/Masterminds/squirrel"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/zerrors"
)
var (
expectedAppQuery = regexp.QuoteMeta(`SELECT projections.apps7.id,` +
expectedAppQueryBase = `SELECT projections.apps7.id,` +
` projections.apps7.name,` +
` projections.apps7.project_id,` +
` projections.apps7.creation_date,` +
@@ -53,8 +56,11 @@ var (
` FROM projections.apps7` +
` LEFT JOIN projections.apps7_api_configs ON projections.apps7.id = projections.apps7_api_configs.app_id AND projections.apps7.instance_id = projections.apps7_api_configs.instance_id` +
` LEFT JOIN projections.apps7_oidc_configs ON projections.apps7.id = projections.apps7_oidc_configs.app_id AND projections.apps7.instance_id = projections.apps7_oidc_configs.instance_id` +
` LEFT JOIN projections.apps7_saml_configs ON projections.apps7.id = projections.apps7_saml_configs.app_id AND projections.apps7.instance_id = projections.apps7_saml_configs.instance_id` +
` AS OF SYSTEM TIME '-1 ms'`)
` LEFT JOIN projections.apps7_saml_configs ON projections.apps7.id = projections.apps7_saml_configs.app_id AND projections.apps7.instance_id = projections.apps7_saml_configs.instance_id`
expectedAppQuery = regexp.QuoteMeta(expectedAppQueryBase)
expectedActiveAppQuery = regexp.QuoteMeta(expectedAppQueryBase +
` LEFT JOIN projections.projects4 ON projections.apps7.project_id = projections.projects4.id AND projections.apps7.instance_id = projections.projects4.instance_id` +
` LEFT JOIN projections.orgs1 ON projections.apps7.resource_owner = projections.orgs1.id AND projections.apps7.instance_id = projections.orgs1.instance_id`)
expectedAppsQuery = regexp.QuoteMeta(`SELECT projections.apps7.id,` +
` projections.apps7.name,` +
` projections.apps7.project_id,` +
@@ -1140,8 +1146,10 @@ func Test_AppPrepare(t *testing.T) {
object interface{}
}{
{
name: "prepareAppQuery no result",
prepare: prepareAppQuery,
name: "prepareAppQuery no result",
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
return prepareAppQuery(ctx, db, false)
},
want: want{
sqlExpectations: mockQueriesScanErr(
expectedAppQuery,
@@ -1158,8 +1166,10 @@ func Test_AppPrepare(t *testing.T) {
object: (*App)(nil),
},
{
name: "prepareAppQuery found",
prepare: prepareAppQuery,
name: "prepareAppQuery found",
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
return prepareAppQuery(ctx, db, false)
},
want: want{
sqlExpectations: mockQuery(
expectedAppQuery,
@@ -1215,8 +1225,10 @@ func Test_AppPrepare(t *testing.T) {
},
},
{
name: "prepareAppQuery api app",
prepare: prepareAppQuery,
name: "prepareAppQuery api app",
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
return prepareAppQuery(ctx, db, false)
},
want: want{
sqlExpectations: mockQueries(
expectedAppQuery,
@@ -1278,8 +1290,10 @@ func Test_AppPrepare(t *testing.T) {
},
},
{
name: "prepareAppQuery oidc app",
prepare: prepareAppQuery,
name: "prepareAppQuery oidc app",
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
return prepareAppQuery(ctx, db, false)
},
want: want{
sqlExpectations: mockQueries(
expectedAppQuery,
@@ -1355,9 +1369,93 @@ func Test_AppPrepare(t *testing.T) {
SkipNativeAppSuccessPage: false,
},
},
}, {
name: "prepareAppQuery saml app",
prepare: prepareAppQuery,
},
{
name: "prepareAppQuery oidc app active only",
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
return prepareAppQuery(ctx, db, true)
},
want: want{
sqlExpectations: mockQueries(
expectedActiveAppQuery,
appCols,
[][]driver.Value{
{
"app-id",
"app-name",
"project-id",
testNow,
testNow,
"ro",
domain.AppStateActive,
uint64(20211109),
// api config
nil,
nil,
nil,
// oidc config
"app-id",
domain.OIDCVersionV1,
"oidc-client-id",
database.TextArray[string]{"https://redirect.to/me"},
database.NumberArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken},
database.NumberArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit},
domain.OIDCApplicationTypeUserAgent,
domain.OIDCAuthMethodTypeNone,
database.TextArray[string]{"post.logout.ch"},
true,
domain.OIDCTokenTypeJWT,
true,
true,
true,
1 * time.Second,
database.TextArray[string]{"additional.origin"},
false,
// saml config
nil,
nil,
nil,
nil,
},
},
),
},
object: &App{
ID: "app-id",
CreationDate: testNow,
ChangeDate: testNow,
ResourceOwner: "ro",
State: domain.AppStateActive,
Sequence: 20211109,
Name: "app-name",
ProjectID: "project-id",
OIDCConfig: &OIDCApp{
Version: domain.OIDCVersionV1,
ClientID: "oidc-client-id",
RedirectURIs: database.TextArray[string]{"https://redirect.to/me"},
ResponseTypes: database.NumberArray[domain.OIDCResponseType]{domain.OIDCResponseTypeIDTokenToken},
GrantTypes: database.NumberArray[domain.OIDCGrantType]{domain.OIDCGrantTypeImplicit},
AppType: domain.OIDCApplicationTypeUserAgent,
AuthMethodType: domain.OIDCAuthMethodTypeNone,
PostLogoutRedirectURIs: database.TextArray[string]{"post.logout.ch"},
IsDevMode: true,
AccessTokenType: domain.OIDCTokenTypeJWT,
AssertAccessTokenRole: true,
AssertIDTokenRole: true,
AssertIDTokenUserinfo: true,
ClockSkew: 1 * time.Second,
AdditionalOrigins: database.TextArray[string]{"additional.origin"},
ComplianceProblems: nil,
AllowedOrigins: database.TextArray[string]{"https://redirect.to", "additional.origin"},
SkipNativeAppSuccessPage: false,
},
},
},
{
name: "prepareAppQuery saml app",
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
return prepareAppQuery(ctx, db, false)
},
want: want{
sqlExpectations: mockQueries(
expectedAppQuery,
@@ -1420,8 +1518,10 @@ func Test_AppPrepare(t *testing.T) {
},
},
{
name: "prepareAppQuery oidc app IsDevMode inactive",
prepare: prepareAppQuery,
name: "prepareAppQuery oidc app IsDevMode inactive",
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
return prepareAppQuery(ctx, db, false)
},
want: want{
sqlExpectations: mockQueries(
expectedAppQuery,
@@ -1499,8 +1599,10 @@ func Test_AppPrepare(t *testing.T) {
},
},
{
name: "prepareAppQuery oidc app AssertAccessTokenRole inactive",
prepare: prepareAppQuery,
name: "prepareAppQuery oidc app AssertAccessTokenRole inactive",
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
return prepareAppQuery(ctx, db, false)
},
want: want{
sqlExpectations: mockQueries(
expectedAppQuery,
@@ -1578,8 +1680,10 @@ func Test_AppPrepare(t *testing.T) {
},
},
{
name: "prepareAppQuery oidc app AssertIDTokenRole inactive",
prepare: prepareAppQuery,
name: "prepareAppQuery oidc app AssertIDTokenRole inactive",
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
return prepareAppQuery(ctx, db, false)
},
want: want{
sqlExpectations: mockQueries(
expectedAppQuery,
@@ -1657,8 +1761,10 @@ func Test_AppPrepare(t *testing.T) {
},
},
{
name: "prepareAppQuery oidc app AssertIDTokenUserinfo inactive",
prepare: prepareAppQuery,
name: "prepareAppQuery oidc app AssertIDTokenUserinfo inactive",
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
return prepareAppQuery(ctx, db, false)
},
want: want{
sqlExpectations: mockQueries(
expectedAppQuery,
@@ -1736,8 +1842,10 @@ func Test_AppPrepare(t *testing.T) {
},
},
{
name: "prepareAppQuery sql err",
prepare: prepareAppQuery,
name: "prepareAppQuery sql err",
prepare: func(ctx context.Context, db prepareDatabase) (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
return prepareAppQuery(ctx, db, false)
},
want: want{
sqlExpectations: mockQueryErr(
expectedAppQuery,