add feature flag to db

This commit is contained in:
conblem 2025-02-13 14:40:08 +01:00
parent 462eff65fa
commit 4007274346
17 changed files with 79 additions and 40 deletions

View File

@ -403,6 +403,34 @@
'SETTING.FEATURES.OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION' | translate
}}</cnsl-info-section>
</div>
<div class="feature-row" *ngIf="toggleStates.consoleUseV2UserApi">
<span>{{ 'SETTING.FEATURES.CONSOLEUSEV2USERAPI' | translate }}</span>
<div class="row">
<mat-button-toggle-group
class="theme-toggle"
class="buttongroup"
[(ngModel)]="toggleStates.consoleUseV2UserApi.state"
(change)="validateAndSave()"
name="displayview"
aria-label="Display View"
>
<mat-button-toggle [value]="ToggleState.DISABLED">
<div class="toggle-row">
<span> {{ 'SETTING.FEATURES.STATES.DISABLED' | translate }}</span>
</div>
</mat-button-toggle>
<mat-button-toggle [value]="ToggleState.ENABLED">
<div class="toggle-row">
<span> {{ 'SETTING.FEATURES.STATES.ENABLED' | translate }}</span>
</div>
</mat-button-toggle>
</mat-button-toggle-group>
</div>
<cnsl-info-section class="feature-info">{{
'SETTING.FEATURES.CONSOLEUSEV2USERAPI_DESCRIPTION' | translate
}}</cnsl-info-section>
</div>
</div>
</cnsl-card>
</div>

View File

@ -16,13 +16,14 @@ import { InfoSectionModule } from 'src/app/modules/info-section/info-section.mod
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { Event } from 'src/app/proto/generated/zitadel/event_pb';
import { Source } from 'src/app/proto/generated/zitadel/feature/v2beta/feature_pb';
import {
GetInstanceFeaturesResponse,
SetInstanceFeaturesRequest,
} from 'src/app/proto/generated/zitadel/feature/v2beta/instance_pb';
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
import { FeatureService } from 'src/app/services/feature.service';
import { ToastService } from 'src/app/services/toast.service';
import {
GetInstanceFeaturesResponse,
SetInstanceFeaturesRequest,
} from '../../proto/generated/zitadel/feature/v2/instance_pb';
import { withIdentifier } from 'codelyzer/util/astQuery';
enum ToggleState {
ENABLED = 'ENABLED',
@ -39,6 +40,7 @@ type ToggleStates = {
oidcTokenExchange?: FeatureState;
actions?: FeatureState;
oidcSingleV1SessionTermination?: FeatureState;
consoleUseV2UserApi?: FeatureState;
};
@Component({
@ -142,6 +144,7 @@ export class FeaturesComponent implements OnDestroy {
);
changed = true;
}
req.setConsoleUseV2UserApi(this.toggleStates?.consoleUseV2UserApi?.state === ToggleState.ENABLED);
if (changed) {
this.featureService
@ -232,6 +235,10 @@ export class FeaturesComponent implements OnDestroy {
? ToggleState.ENABLED
: ToggleState.DISABLED,
},
consoleUseV2UserApi: {
source: this.featureData.consoleUseV2UserApi?.source || Source.SOURCE_INSTANCE,
state: this.featureData.consoleUseV2UserApi?.enabled ? ToggleState.ENABLED : ToggleState.DISABLED,
},
};
});
}

View File

@ -1,19 +1,19 @@
import { Injectable } from '@angular/core';
import { GrpcService } from './grpc.service';
import {
GetInstanceFeaturesRequest,
GetInstanceFeaturesResponse,
ResetInstanceFeaturesRequest,
SetInstanceFeaturesRequest,
SetInstanceFeaturesResponse,
} from '../proto/generated/zitadel/feature/v2beta/instance_pb';
import {
GetOrganizationFeaturesRequest,
GetOrganizationFeaturesResponse,
} from '../proto/generated/zitadel/feature/v2beta/organization_pb';
import { GetUserFeaturesRequest, GetUserFeaturesResponse } from '../proto/generated/zitadel/feature/v2beta/user_pb';
import { GetSystemFeaturesRequest, GetSystemFeaturesResponse } from '../proto/generated/zitadel/feature/v2beta/system_pb';
import {
GetInstanceFeaturesRequest,
GetInstanceFeaturesResponse,
ResetInstanceFeaturesRequest,
SetInstanceFeaturesRequest,
SetInstanceFeaturesResponse,
} from '../proto/generated/zitadel/feature/v2/instance_pb';
@Injectable({
providedIn: 'root',

View File

@ -16,13 +16,13 @@ import { ExhaustedGrpcInterceptor } from './interceptors/exhausted.grpc.intercep
import { I18nInterceptor } from './interceptors/i18n.interceptor';
import { OrgInterceptor } from './interceptors/org.interceptor';
import { StorageService } from './storage.service';
import { FeatureServiceClient } from '../proto/generated/zitadel/feature/v2beta/Feature_serviceServiceClientPb';
import { UserServiceClient } from '../proto/generated/zitadel/user/v2/User_serviceServiceClientPb';
//@ts-ignore
import { createUserServiceClient } from '@zitadel/client/v2';
//@ts-ignore
import { createAuthServiceClient, createManagementServiceClient } from '@zitadel/client/v1';
import { createGrpcWebTransport } from '@connectrpc/connect-web';
import { FeatureServiceClient } from '../proto/generated/zitadel/feature/v2/Feature_serviceServiceClientPb';
@Injectable({
providedIn: 'root',

View File

@ -1494,7 +1494,9 @@
"ENABLED": "\"Enabled\" is inherited",
"DISABLED": "\"Disabled\" is inherited"
},
"RESET": "Set all to inherit"
"RESET": "Set all to inherit",
"CONSOLEUSEV2USERAPI": "Use V2 Api in Console for User creation",
"CONSOLEUSEV2USERAPI_DESCRIPTION": "When this flag is enabled, the console uses the V2 User API to create new users. With the V2 API, newly created users start without an initial state."
},
"DIALOG": {
"RESET": {

View File

@ -71,6 +71,7 @@ func instanceFeaturesToCommand(req *feature_pb.SetInstanceFeaturesRequest) (*com
EnableBackChannelLogout: req.EnableBackChannelLogout,
LoginV2: loginV2,
PermissionCheckV2: req.PermissionCheckV2,
ConsoleUseV2UserApi: req.ConsoleUseV2UserApi,
}, nil
}
@ -91,10 +92,7 @@ func instanceFeaturesToPb(f *query.InstanceFeatures) *feature_pb.GetInstanceFeat
EnableBackChannelLogout: featureSourceToFlagPb(&f.EnableBackChannelLogout),
LoginV2: loginV2ToLoginV2FlagPb(f.LoginV2),
PermissionCheckV2: featureSourceToFlagPb(&f.PermissionCheckV2),
ConsoleUseV2UserApi: &feature_pb.FeatureFlag{
Enabled: true,
Source: feature_pb.Source_SOURCE_INSTANCE,
},
ConsoleUseV2UserApi: featureSourceToFlagPb(&f.ConsoleUseV2UserApi),
}
}

View File

@ -63,10 +63,6 @@ func instanceFeaturesToPb(f *query.InstanceFeatures) *feature_pb.GetInstanceFeat
WebKey: featureSourceToFlagPb(&f.WebKey),
DebugOidcParentError: featureSourceToFlagPb(&f.DebugOIDCParentError),
OidcSingleV1SessionTermination: featureSourceToFlagPb(&f.OIDCSingleV1SessionTermination),
ConsoleUseV2UserApi: &feature_pb.FeatureFlag{
Enabled: true,
Source: feature_pb.Source_SOURCE_INSTANCE,
},
}
}

View File

@ -30,6 +30,7 @@ type InstanceFeatures struct {
EnableBackChannelLogout *bool
LoginV2 *feature.LoginV2
PermissionCheckV2 *bool
ConsoleUseV2UserApi *bool
}
func (m *InstanceFeatures) isEmpty() bool {
@ -47,7 +48,7 @@ func (m *InstanceFeatures) isEmpty() bool {
m.DisableUserTokenEvent == nil &&
m.EnableBackChannelLogout == nil &&
m.LoginV2 == nil &&
m.PermissionCheckV2 == nil
m.PermissionCheckV2 == nil && m.ConsoleUseV2UserApi == nil
}
func (c *Commands) SetInstanceFeatures(ctx context.Context, f *InstanceFeatures) (*domain.ObjectDetails, error) {

View File

@ -80,6 +80,7 @@ func (m *InstanceFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder {
feature_v2.InstanceEnableBackChannelLogout,
feature_v2.InstanceLoginVersion,
feature_v2.InstancePermissionCheckV2,
feature_v2.InstanceConsoleUseV2UserApi,
).
Builder().ResourceOwner(m.ResourceOwner)
}
@ -133,6 +134,9 @@ func reduceInstanceFeature(features *InstanceFeatures, key feature.Key, value an
case feature.KeyPermissionCheckV2:
v := value.(bool)
features.PermissionCheckV2 = &v
case feature.KeyConsoleUseV2UserApi:
v := value.(bool)
features.ConsoleUseV2UserApi = &v
}
}
@ -153,5 +157,6 @@ func (wm *InstanceFeaturesWriteModel) setCommands(ctx context.Context, f *Instan
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.EnableBackChannelLogout, f.EnableBackChannelLogout, feature_v2.InstanceEnableBackChannelLogout)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.LoginV2, f.LoginV2, feature_v2.InstanceLoginVersion)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.PermissionCheckV2, f.PermissionCheckV2, feature_v2.InstancePermissionCheckV2)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.ConsoleUseV2UserApi, f.ConsoleUseV2UserApi, feature_v2.InstanceConsoleUseV2UserApi)
return cmds
}

View File

@ -24,6 +24,7 @@ const (
KeyEnableBackChannelLogout
KeyLoginV2
KeyPermissionCheckV2
KeyConsoleUseV2UserApi
)
//go:generate enumer -type Level -transform snake -trimprefix Level
@ -54,6 +55,7 @@ type Features struct {
EnableBackChannelLogout bool `json:"enable_back_channel_logout,omitempty"`
LoginV2 LoginV2 `json:"login_v2,omitempty"`
PermissionCheckV2 bool `json:"permission_check_v2,omitempty"`
ConsoleUseV2UserApi bool `json:"console_use_v2_user_api,omitempty"`
}
type ImprovedPerformanceType int32

View File

@ -7,11 +7,11 @@ import (
"strings"
)
const _KeyName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspectionuser_schematoken_exchangeactionsimproved_performanceweb_keydebug_oidc_parent_erroroidc_single_v1_session_terminationdisable_user_token_eventenable_back_channel_logoutlogin_v2permission_check_v2"
const _KeyName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspectionuser_schematoken_exchangeactionsimproved_performanceweb_keydebug_oidc_parent_erroroidc_single_v1_session_terminationdisable_user_token_eventenable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_api"
var _KeyIndex = [...]uint16{0, 11, 28, 61, 81, 92, 106, 113, 133, 140, 163, 197, 221, 247, 255, 274}
var _KeyIndex = [...]uint16{0, 11, 28, 61, 81, 92, 106, 113, 133, 140, 163, 197, 221, 247, 255, 274, 297}
const _KeyLowerName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspectionuser_schematoken_exchangeactionsimproved_performanceweb_keydebug_oidc_parent_erroroidc_single_v1_session_terminationdisable_user_token_eventenable_back_channel_logoutlogin_v2permission_check_v2"
const _KeyLowerName = "unspecifiedlogin_default_orgtrigger_introspection_projectionslegacy_introspectionuser_schematoken_exchangeactionsimproved_performanceweb_keydebug_oidc_parent_erroroidc_single_v1_session_terminationdisable_user_token_eventenable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_api"
func (i Key) String() string {
if i < 0 || i >= Key(len(_KeyIndex)-1) {
@ -39,9 +39,10 @@ func _KeyNoOp() {
_ = x[KeyEnableBackChannelLogout-(12)]
_ = x[KeyLoginV2-(13)]
_ = x[KeyPermissionCheckV2-(14)]
_ = x[KeyConsoleUseV2UserApi-(15)]
}
var _KeyValues = []Key{KeyUnspecified, KeyLoginDefaultOrg, KeyTriggerIntrospectionProjections, KeyLegacyIntrospection, KeyUserSchema, KeyTokenExchange, KeyActions, KeyImprovedPerformance, KeyWebKey, KeyDebugOIDCParentError, KeyOIDCSingleV1SessionTermination, KeyDisableUserTokenEvent, KeyEnableBackChannelLogout, KeyLoginV2, KeyPermissionCheckV2}
var _KeyValues = []Key{KeyUnspecified, KeyLoginDefaultOrg, KeyTriggerIntrospectionProjections, KeyLegacyIntrospection, KeyUserSchema, KeyTokenExchange, KeyActions, KeyImprovedPerformance, KeyWebKey, KeyDebugOIDCParentError, KeyOIDCSingleV1SessionTermination, KeyDisableUserTokenEvent, KeyEnableBackChannelLogout, KeyLoginV2, KeyPermissionCheckV2, KeyConsoleUseV2UserApi}
var _KeyNameToValueMap = map[string]Key{
_KeyName[0:11]: KeyUnspecified,
@ -74,6 +75,8 @@ var _KeyNameToValueMap = map[string]Key{
_KeyLowerName[247:255]: KeyLoginV2,
_KeyName[255:274]: KeyPermissionCheckV2,
_KeyLowerName[255:274]: KeyPermissionCheckV2,
_KeyName[274:297]: KeyConsoleUseV2UserApi,
_KeyLowerName[274:297]: KeyConsoleUseV2UserApi,
}
var _KeyNames = []string{
@ -92,6 +95,7 @@ var _KeyNames = []string{
_KeyName[221:247],
_KeyName[247:255],
_KeyName[255:274],
_KeyName[274:297],
}
// KeyString retrieves an enum value from the enum constants string name.

View File

@ -23,6 +23,7 @@ type InstanceFeatures struct {
EnableBackChannelLogout FeatureSource[bool]
LoginV2 FeatureSource[*feature.LoginV2]
PermissionCheckV2 FeatureSource[bool]
ConsoleUseV2UserApi FeatureSource[bool]
}
func (q *Queries) GetInstanceFeatures(ctx context.Context, cascade bool) (_ *InstanceFeatures, err error) {

View File

@ -76,6 +76,7 @@ func (m *InstanceFeaturesReadModel) Query() *eventstore.SearchQueryBuilder {
feature_v2.InstanceEnableBackChannelLogout,
feature_v2.InstanceLoginVersion,
feature_v2.InstancePermissionCheckV2,
feature_v2.InstanceConsoleUseV2UserApi,
).
Builder().ResourceOwner(m.ResourceOwner)
}
@ -142,6 +143,8 @@ func reduceInstanceFeatureSet[T any](features *InstanceFeatures, event *feature_
features.LoginV2.set(level, event.Value)
case feature.KeyPermissionCheckV2:
features.PermissionCheckV2.set(level, event.Value)
case feature.KeyConsoleUseV2UserApi:
features.ConsoleUseV2UserApi.set(level, event.Value)
}
return nil
}

View File

@ -116,6 +116,10 @@ func (*instanceFeatureProjection) Reducers() []handler.AggregateReducer {
Event: feature_v2.InstancePermissionCheckV2,
Reduce: reduceInstanceSetFeature[bool],
},
{
Event: feature_v2.InstanceConsoleUseV2UserApi,
Reduce: reduceInstanceSetFeature[bool],
},
{
Event: instance.InstanceRemovedEventType,
Reduce: reduceInstanceRemovedHelper(InstanceDomainInstanceIDCol),

View File

@ -35,4 +35,5 @@ func init() {
eventstore.RegisterFilterEventMapper(AggregateType, InstanceEnableBackChannelLogout, eventstore.GenericEventMapper[SetEvent[bool]])
eventstore.RegisterFilterEventMapper(AggregateType, InstanceLoginVersion, eventstore.GenericEventMapper[SetEvent[*feature.LoginV2]])
eventstore.RegisterFilterEventMapper(AggregateType, InstancePermissionCheckV2, eventstore.GenericEventMapper[SetEvent[bool]])
eventstore.RegisterFilterEventMapper(AggregateType, InstanceConsoleUseV2UserApi, eventstore.GenericEventMapper[SetEvent[bool]])
}

View File

@ -40,6 +40,7 @@ var (
InstanceEnableBackChannelLogout = setEventTypeFromFeature(feature.LevelInstance, feature.KeyEnableBackChannelLogout)
InstanceLoginVersion = setEventTypeFromFeature(feature.LevelInstance, feature.KeyLoginV2)
InstancePermissionCheckV2 = setEventTypeFromFeature(feature.LevelInstance, feature.KeyPermissionCheckV2)
InstanceConsoleUseV2UserApi = setEventTypeFromFeature(feature.LevelInstance, feature.KeyConsoleUseV2UserApi)
)
const (

View File

@ -79,13 +79,6 @@ message SetInstanceFeaturesRequest{
description: "If the flag is enabled, you'll be able to terminate a single session from the login UI by providing an id_token with a `sid` claim as id_token_hint on the end_session endpoint. Note that currently all sessions from the same user agent (browser) are terminated in the login UI. Sessions managed through the Session API already allow the termination of single sessions.";
}
];
optional bool console_use_v2_user_api = 11 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "true";
description: "If this is enabled the console web client will use the new User v2 API for certain calls";
}
];
}
message SetInstanceFeaturesResponse {
@ -178,11 +171,4 @@ message GetInstanceFeaturesResponse {
description: "If the flag is enabled, you'll be able to terminate a single session from the login UI by providing an id_token with a `sid` claim as id_token_hint on the end_session endpoint. Note that currently all sessions from the same user agent (browser) are terminated in the login UI. Sessions managed through the Session API already allow the termination of single sessions.";
}
];
FeatureFlag console_use_v2_user_api = 12 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "true";
description: "If this is enabled the console web client will use the new User v2 API for certain calls";
}
];
}