mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 19:17:32 +00:00
feat: restrict languages (#6931)
* feat: return 404 or 409 if org reg disallowed * fix: system limit permissions * feat: add iam limits api * feat: disallow public org registrations on default instance * add integration test * test: integration * fix test * docs: describe public org registrations * avoid updating docs deps * fix system limits integration test * silence integration tests * fix linting * ignore strange linter complaints * review * improve reset properties naming * redefine the api * use restrictions aggregate * test query * simplify and test projection * test commands * fix unit tests * move integration test * support restrictions on default instance * also test GetRestrictions * self review * lint * abstract away resource owner * fix tests * configure supported languages * fix allowed languages * fix tests * default lang must not be restricted * preferred language must be allowed * change preferred languages * check languages everywhere * lint * test command side * lint * add integration test * add integration test * restrict supported ui locales * lint * lint * cleanup * lint * allow undefined preferred language * fix integration tests * update main * fix env var * ignore linter * ignore linter * improve integration test config * reduce cognitive complexity * compile * check for duplicates * remove useless restriction checks * review * revert restriction renaming * fix language restrictions * lint * generate * allow custom texts for supported langs for now * fix tests * cleanup * cleanup * cleanup * lint * unsupported preferred lang is allowed * fix integration test * finish reverting to old property name * finish reverting to old property name * load languages * refactor(i18n): centralize translators and fs * lint * amplify no validations on preferred languages * fix integration test * lint * fix resetting allowed languages * test unchanged restrictions
This commit is contained in:
@@ -85,6 +85,21 @@ func (mr *MockQueriesMockRecorder) GetDefaultLanguage(arg0 any) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultLanguage", reflect.TypeOf((*MockQueries)(nil).GetDefaultLanguage), arg0)
|
||||
}
|
||||
|
||||
// GetInstanceRestrictions mocks base method.
|
||||
func (m *MockQueries) GetInstanceRestrictions(arg0 context.Context) (query.Restrictions, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetInstanceRestrictions", arg0)
|
||||
ret0, _ := ret[0].(query.Restrictions)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetInstanceRestrictions indicates an expected call of GetInstanceRestrictions.
|
||||
func (mr *MockQueriesMockRecorder) GetInstanceRestrictions(arg0 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInstanceRestrictions", reflect.TypeOf((*MockQueries)(nil).GetInstanceRestrictions), arg0)
|
||||
}
|
||||
|
||||
// GetNotifyUserByID mocks base method.
|
||||
func (m *MockQueries) GetNotifyUserByID(arg0 context.Context, arg1 bool, arg2 string, arg3 ...query.SearchQuery) (*query.NotifyUser, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@@ -2,8 +2,6 @@ package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
@@ -25,6 +23,7 @@ type Queries interface {
|
||||
SMSProviderConfig(ctx context.Context, queries ...query.SearchQuery) (*query.SMSConfig, error)
|
||||
SMTPConfigByAggregateID(ctx context.Context, aggregateID string) (*query.SMTPConfig, error)
|
||||
GetDefaultLanguage(ctx context.Context) language.Tag
|
||||
GetInstanceRestrictions(ctx context.Context) (restrictions query.Restrictions, err error)
|
||||
}
|
||||
|
||||
type NotificationQueries struct {
|
||||
@@ -37,7 +36,6 @@ type NotificationQueries struct {
|
||||
UserDataCrypto crypto.EncryptionAlgorithm
|
||||
SMTPPasswordCrypto crypto.EncryptionAlgorithm
|
||||
SMSTokenCrypto crypto.EncryptionAlgorithm
|
||||
statikDir http.FileSystem
|
||||
}
|
||||
|
||||
func NewNotificationQueries(
|
||||
@@ -50,7 +48,6 @@ func NewNotificationQueries(
|
||||
userDataCrypto crypto.EncryptionAlgorithm,
|
||||
smtpPasswordCrypto crypto.EncryptionAlgorithm,
|
||||
smsTokenCrypto crypto.EncryptionAlgorithm,
|
||||
statikDir http.FileSystem,
|
||||
) *NotificationQueries {
|
||||
return &NotificationQueries{
|
||||
Queries: baseQueries,
|
||||
@@ -62,6 +59,5 @@ func NewNotificationQueries(
|
||||
UserDataCrypto: userDataCrypto,
|
||||
SMTPPasswordCrypto: smtpPasswordCrypto,
|
||||
SMSTokenCrypto: smsTokenCrypto,
|
||||
statikDir: statikDir,
|
||||
}
|
||||
}
|
||||
|
@@ -10,7 +10,11 @@ import (
|
||||
)
|
||||
|
||||
func (n *NotificationQueries) GetTranslatorWithOrgTexts(ctx context.Context, orgID, textType string) (*i18n.Translator, error) {
|
||||
translator, err := i18n.NewTranslator(n.statikDir, n.GetDefaultLanguage(ctx), "")
|
||||
restrictions, err := n.Queries.GetInstanceRestrictions(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
translator, err := i18n.NewNotificationTranslator(n.GetDefaultLanguage(ctx), restrictions.AllowedLanguages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -12,7 +11,6 @@ import (
|
||||
|
||||
"github.com/zitadel/zitadel/internal/notification/messages"
|
||||
|
||||
statik_fs "github.com/rakyll/statik/fs"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
"golang.org/x/text/language"
|
||||
@@ -202,15 +200,13 @@ func Test_userNotifier_reduceInitCodeAdded(t *testing.T) {
|
||||
},
|
||||
}}
|
||||
// TODO: Why don't we have an url template on user.HumanInitialCodeAddedEvent?
|
||||
fs, err := statik_fs.NewWithNamespace("notification")
|
||||
assert.NoError(t, err)
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
queries := mock.NewMockQueries(ctrl)
|
||||
commands := mock.NewMockCommands(ctrl)
|
||||
f, a, w := tt.test(ctrl, queries, commands)
|
||||
stmt, err := newUserNotifier(t, ctrl, queries, fs, f, a, w).reduceInitCodeAdded(a.event)
|
||||
stmt, err := newUserNotifier(t, ctrl, queries, f, a, w).reduceInitCodeAdded(a.event)
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -423,15 +419,13 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) {
|
||||
}, w
|
||||
},
|
||||
}}
|
||||
fs, err := statik_fs.NewWithNamespace("notification")
|
||||
assert.NoError(t, err)
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
queries := mock.NewMockQueries(ctrl)
|
||||
commands := mock.NewMockCommands(ctrl)
|
||||
f, a, w := tt.test(ctrl, queries, commands)
|
||||
stmt, err := newUserNotifier(t, ctrl, queries, fs, f, a, w).reduceEmailCodeAdded(a.event)
|
||||
stmt, err := newUserNotifier(t, ctrl, queries, f, a, w).reduceEmailCodeAdded(a.event)
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -644,15 +638,13 @@ func Test_userNotifier_reducePasswordCodeAdded(t *testing.T) {
|
||||
}, w
|
||||
},
|
||||
}}
|
||||
fs, err := statik_fs.NewWithNamespace("notification")
|
||||
assert.NoError(t, err)
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
queries := mock.NewMockQueries(ctrl)
|
||||
commands := mock.NewMockCommands(ctrl)
|
||||
f, a, w := tt.test(ctrl, queries, commands)
|
||||
stmt, err := newUserNotifier(t, ctrl, queries, fs, f, a, w).reducePasswordCodeAdded(a.event)
|
||||
stmt, err := newUserNotifier(t, ctrl, queries, f, a, w).reducePasswordCodeAdded(a.event)
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -737,15 +729,13 @@ func Test_userNotifier_reduceDomainClaimed(t *testing.T) {
|
||||
}, w
|
||||
},
|
||||
}}
|
||||
fs, err := statik_fs.NewWithNamespace("notification")
|
||||
assert.NoError(t, err)
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
queries := mock.NewMockQueries(ctrl)
|
||||
commands := mock.NewMockCommands(ctrl)
|
||||
f, a, w := tt.test(ctrl, queries, commands)
|
||||
stmt, err := newUserNotifier(t, ctrl, queries, fs, f, a, w).reduceDomainClaimed(a.event)
|
||||
stmt, err := newUserNotifier(t, ctrl, queries, f, a, w).reduceDomainClaimed(a.event)
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -963,15 +953,13 @@ func Test_userNotifier_reducePasswordlessCodeRequested(t *testing.T) {
|
||||
}, w
|
||||
},
|
||||
}}
|
||||
fs, err := statik_fs.NewWithNamespace("notification")
|
||||
assert.NoError(t, err)
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
queries := mock.NewMockQueries(ctrl)
|
||||
commands := mock.NewMockCommands(ctrl)
|
||||
f, a, w := tt.test(ctrl, queries, commands)
|
||||
stmt, err := newUserNotifier(t, ctrl, queries, fs, f, a, w).reducePasswordlessCodeRequested(a.event)
|
||||
stmt, err := newUserNotifier(t, ctrl, queries, f, a, w).reducePasswordlessCodeRequested(a.event)
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -1062,15 +1050,13 @@ func Test_userNotifier_reducePasswordChanged(t *testing.T) {
|
||||
}, w
|
||||
},
|
||||
}}
|
||||
fs, err := statik_fs.NewWithNamespace("notification")
|
||||
assert.NoError(t, err)
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
queries := mock.NewMockQueries(ctrl)
|
||||
commands := mock.NewMockCommands(ctrl)
|
||||
f, a, w := tt.test(ctrl, queries, commands)
|
||||
stmt, err := newUserNotifier(t, ctrl, queries, fs, f, a, w).reducePasswordChanged(a.event)
|
||||
stmt, err := newUserNotifier(t, ctrl, queries, f, a, w).reducePasswordChanged(a.event)
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -1287,15 +1273,13 @@ func Test_userNotifier_reduceOTPEmailChallenged(t *testing.T) {
|
||||
}, w
|
||||
},
|
||||
}}
|
||||
fs, err := statik_fs.NewWithNamespace("notification")
|
||||
assert.NoError(t, err)
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
queries := mock.NewMockQueries(ctrl)
|
||||
commands := mock.NewMockCommands(ctrl)
|
||||
f, a, w := tt.test(ctrl, queries, commands)
|
||||
_, err = newUserNotifier(t, ctrl, queries, fs, f, a, w).reduceSessionOTPEmailChallenged(a.event)
|
||||
_, err := newUserNotifier(t, ctrl, queries, f, a, w).reduceSessionOTPEmailChallenged(a.event)
|
||||
if w.err != nil {
|
||||
w.err(t, err)
|
||||
} else {
|
||||
@@ -1320,7 +1304,7 @@ type want struct {
|
||||
err assert.ErrorAssertionFunc
|
||||
}
|
||||
|
||||
func newUserNotifier(t *testing.T, ctrl *gomock.Controller, queries *mock.MockQueries, fs http.FileSystem, f fields, a args, w want) *userNotifier {
|
||||
func newUserNotifier(t *testing.T, ctrl *gomock.Controller, queries *mock.MockQueries, f fields, a args, w want) *userNotifier {
|
||||
queries.EXPECT().NotificationProviderByIDAndType(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&query.DebugNotificationProvider{}, nil)
|
||||
smtpAlg, _ := cryptoValue(t, ctrl, "smtppw")
|
||||
channel := channel_mock.NewMockNotificationChannel(ctrl)
|
||||
@@ -1340,7 +1324,6 @@ func newUserNotifier(t *testing.T, ctrl *gomock.Controller, queries *mock.MockQu
|
||||
f.userDataCrypto,
|
||||
smtpAlg,
|
||||
f.SMSTokenCrypto,
|
||||
fs,
|
||||
),
|
||||
otpEmailTmpl: defaultOTPEmailTemplate,
|
||||
channels: &channels{Chain: *senders.ChainChannels(channel)},
|
||||
@@ -1366,6 +1349,9 @@ func (c *channels) Webhook(context.Context, webhook.Config) (*senders.Chain, err
|
||||
}
|
||||
|
||||
func expectTemplateQueries(queries *mock.MockQueries, template string) {
|
||||
queries.EXPECT().GetInstanceRestrictions(gomock.Any()).Return(query.Restrictions{
|
||||
AllowedLanguages: []language.Tag{language.English},
|
||||
}, nil)
|
||||
queries.EXPECT().ActiveLabelPolicyByOrg(gomock.Any(), gomock.Any(), gomock.Any()).Return(&query.LabelPolicy{
|
||||
ID: policyID,
|
||||
Light: query.Theme{
|
||||
|
Reference in New Issue
Block a user