mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-14 11:58:02 +00:00
dd33538c0a
* 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
259 lines
10 KiB
Go
259 lines
10 KiB
Go
//go:build integration
|
|
|
|
package admin_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/zitadel/zitadel/internal/integration"
|
|
"github.com/zitadel/zitadel/pkg/grpc/admin"
|
|
"github.com/zitadel/zitadel/pkg/grpc/management"
|
|
"github.com/zitadel/zitadel/pkg/grpc/text"
|
|
"github.com/zitadel/zitadel/pkg/grpc/user"
|
|
"golang.org/x/text/language"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
"io"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestServer_Restrictions_AllowedLanguages(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
|
|
defer cancel()
|
|
|
|
var (
|
|
defaultAndAllowedLanguage = language.German
|
|
supportedLanguagesStr = []string{language.German.String(), language.English.String(), language.Japanese.String()}
|
|
disallowedLanguage = language.Spanish
|
|
unsupportedLanguage1 = language.Afrikaans
|
|
unsupportedLanguage2 = language.Albanian
|
|
)
|
|
|
|
domain, _, iamOwnerCtx := Tester.UseIsolatedInstance(ctx, SystemCTX)
|
|
t.Run("assumed defaults are correct", func(tt *testing.T) {
|
|
tt.Run("languages are not restricted by default", func(ttt *testing.T) {
|
|
restrictions, err := Tester.Client.Admin.GetRestrictions(iamOwnerCtx, &admin.GetRestrictionsRequest{})
|
|
require.NoError(ttt, err)
|
|
require.Len(ttt, restrictions.AllowedLanguages, 0)
|
|
})
|
|
tt.Run("default language is English by default", func(ttt *testing.T) {
|
|
defaultLang, err := Tester.Client.Admin.GetDefaultLanguage(iamOwnerCtx, &admin.GetDefaultLanguageRequest{})
|
|
require.NoError(ttt, err)
|
|
require.Equal(ttt, language.Make(defaultLang.Language), language.English)
|
|
})
|
|
tt.Run("the discovery endpoint returns all supported languages", func(ttt *testing.T) {
|
|
checkDiscoveryEndpoint(ttt, domain, supportedLanguagesStr, nil)
|
|
})
|
|
})
|
|
t.Run("restricting the default language fails", func(tt *testing.T) {
|
|
_, err := Tester.Client.Admin.SetRestrictions(iamOwnerCtx, &admin.SetRestrictionsRequest{AllowedLanguages: &admin.SelectLanguages{List: []string{defaultAndAllowedLanguage.String()}}})
|
|
expectStatus, ok := status.FromError(err)
|
|
require.True(tt, ok)
|
|
require.Equal(tt, codes.FailedPrecondition, expectStatus.Code())
|
|
})
|
|
t.Run("not defining any restrictions throws an error", func(tt *testing.T) {
|
|
_, err := Tester.Client.Admin.SetRestrictions(iamOwnerCtx, &admin.SetRestrictionsRequest{})
|
|
expectStatus, ok := status.FromError(err)
|
|
require.True(tt, ok)
|
|
require.Equal(tt, codes.InvalidArgument, expectStatus.Code())
|
|
})
|
|
t.Run("setting the default language works", func(tt *testing.T) {
|
|
setAndAwaitDefaultLanguage(iamOwnerCtx, tt, defaultAndAllowedLanguage)
|
|
})
|
|
t.Run("restricting allowed languages works", func(tt *testing.T) {
|
|
setAndAwaitAllowedLanguages(iamOwnerCtx, tt, []string{defaultAndAllowedLanguage.String()})
|
|
})
|
|
t.Run("setting the default language to a disallowed language fails", func(tt *testing.T) {
|
|
_, err := Tester.Client.Admin.SetDefaultLanguage(iamOwnerCtx, &admin.SetDefaultLanguageRequest{Language: disallowedLanguage.String()})
|
|
expectStatus, ok := status.FromError(err)
|
|
require.True(tt, ok)
|
|
require.Equal(tt, codes.FailedPrecondition, expectStatus.Code())
|
|
})
|
|
t.Run("the list of supported languages includes the disallowed languages", func(tt *testing.T) {
|
|
supported, err := Tester.Client.Admin.GetSupportedLanguages(iamOwnerCtx, &admin.GetSupportedLanguagesRequest{})
|
|
require.NoError(tt, err)
|
|
require.Condition(tt, contains(supported.GetLanguages(), supportedLanguagesStr))
|
|
})
|
|
t.Run("the disallowed language is not listed in the discovery endpoint", func(tt *testing.T) {
|
|
checkDiscoveryEndpoint(tt, domain, []string{defaultAndAllowedLanguage.String()}, []string{disallowedLanguage.String()})
|
|
})
|
|
t.Run("the login ui is rendered in the default language", func(tt *testing.T) {
|
|
checkLoginUILanguage(tt, domain, disallowedLanguage, defaultAndAllowedLanguage, "Allgemeine Geschäftsbedingungen und Datenschutz")
|
|
})
|
|
t.Run("preferred languages are not restricted by the supported languages", func(tt *testing.T) {
|
|
var importedUser *management.ImportHumanUserResponse
|
|
tt.Run("import user", func(ttt *testing.T) {
|
|
var err error
|
|
importedUser, err = importUser(iamOwnerCtx, unsupportedLanguage1)
|
|
require.NoError(ttt, err)
|
|
})
|
|
tt.Run("change user profile", func(ttt *testing.T) {
|
|
_, err := Tester.Client.Mgmt.UpdateHumanProfile(iamOwnerCtx, &management.UpdateHumanProfileRequest{
|
|
UserId: importedUser.GetUserId(),
|
|
FirstName: "hodor",
|
|
LastName: "hodor",
|
|
NickName: integration.RandString(5),
|
|
DisplayName: "hodor",
|
|
PreferredLanguage: unsupportedLanguage2.String(),
|
|
Gender: user.Gender_GENDER_MALE,
|
|
})
|
|
require.NoError(ttt, err)
|
|
})
|
|
})
|
|
t.Run("custom texts are only restricted by the supported languages", func(tt *testing.T) {
|
|
_, err := Tester.Client.Admin.SetCustomLoginText(iamOwnerCtx, &admin.SetCustomLoginTextsRequest{
|
|
Language: disallowedLanguage.String(),
|
|
EmailVerificationText: &text.EmailVerificationScreenText{
|
|
Description: "hodor",
|
|
},
|
|
})
|
|
assert.NoError(tt, err)
|
|
_, err = Tester.Client.Mgmt.SetCustomLoginText(iamOwnerCtx, &management.SetCustomLoginTextsRequest{
|
|
Language: disallowedLanguage.String(),
|
|
EmailVerificationText: &text.EmailVerificationScreenText{
|
|
Description: "hodor",
|
|
},
|
|
})
|
|
assert.NoError(tt, err)
|
|
_, err = Tester.Client.Mgmt.SetCustomInitMessageText(iamOwnerCtx, &management.SetCustomInitMessageTextRequest{
|
|
Language: disallowedLanguage.String(),
|
|
Text: "hodor",
|
|
})
|
|
assert.NoError(tt, err)
|
|
_, err = Tester.Client.Admin.SetDefaultInitMessageText(iamOwnerCtx, &admin.SetDefaultInitMessageTextRequest{
|
|
Language: disallowedLanguage.String(),
|
|
Text: "hodor",
|
|
})
|
|
assert.NoError(tt, err)
|
|
})
|
|
t.Run("allowing all languages works", func(tt *testing.T) {
|
|
tt.Run("restricting allowed languages works", func(ttt *testing.T) {
|
|
setAndAwaitAllowedLanguages(iamOwnerCtx, ttt, make([]string, 0))
|
|
})
|
|
})
|
|
|
|
t.Run("allowing the language makes it usable again", func(tt *testing.T) {
|
|
tt.Run("the disallowed language is listed in the discovery endpoint again", func(ttt *testing.T) {
|
|
checkDiscoveryEndpoint(ttt, domain, []string{defaultAndAllowedLanguage.String()}, []string{disallowedLanguage.String()})
|
|
})
|
|
tt.Run("the login ui is rendered in the allowed language", func(ttt *testing.T) {
|
|
checkLoginUILanguage(ttt, domain, disallowedLanguage, disallowedLanguage, "Términos y condiciones")
|
|
})
|
|
})
|
|
}
|
|
|
|
func setAndAwaitAllowedLanguages(ctx context.Context, t *testing.T, selectLanguages []string) {
|
|
_, err := Tester.Client.Admin.SetRestrictions(ctx, &admin.SetRestrictionsRequest{AllowedLanguages: &admin.SelectLanguages{List: selectLanguages}})
|
|
require.NoError(t, err)
|
|
awaitCtx, awaitCancel := context.WithTimeout(ctx, 10*time.Second)
|
|
defer awaitCancel()
|
|
await(t, awaitCtx, func() bool {
|
|
restrictions, getErr := Tester.Client.Admin.GetRestrictions(awaitCtx, &admin.GetRestrictionsRequest{})
|
|
expectLanguages := selectLanguages
|
|
if len(selectLanguages) == 0 {
|
|
expectLanguages = nil
|
|
}
|
|
return assert.NoError(NoopAssertionT, getErr) &&
|
|
assert.Equal(NoopAssertionT, expectLanguages, restrictions.GetAllowedLanguages())
|
|
})
|
|
}
|
|
|
|
func setAndAwaitDefaultLanguage(ctx context.Context, t *testing.T, lang language.Tag) {
|
|
_, err := Tester.Client.Admin.SetDefaultLanguage(ctx, &admin.SetDefaultLanguageRequest{Language: lang.String()})
|
|
require.NoError(t, err)
|
|
awaitCtx, awaitCancel := context.WithTimeout(ctx, 10*time.Second)
|
|
defer awaitCancel()
|
|
await(t, awaitCtx, func() bool {
|
|
defaultLang, getErr := Tester.Client.Admin.GetDefaultLanguage(awaitCtx, &admin.GetDefaultLanguageRequest{})
|
|
return assert.NoError(NoopAssertionT, getErr) &&
|
|
assert.Equal(NoopAssertionT, lang.String(), defaultLang.GetLanguage())
|
|
})
|
|
}
|
|
|
|
func importUser(ctx context.Context, preferredLanguage language.Tag) (*management.ImportHumanUserResponse, error) {
|
|
random := integration.RandString(5)
|
|
return Tester.Client.Mgmt.ImportHumanUser(ctx, &management.ImportHumanUserRequest{
|
|
UserName: "integration-test-user_" + random,
|
|
Profile: &management.ImportHumanUserRequest_Profile{
|
|
FirstName: "hodor",
|
|
LastName: "hodor",
|
|
NickName: "hodor",
|
|
PreferredLanguage: preferredLanguage.String(),
|
|
},
|
|
Email: &management.ImportHumanUserRequest_Email{
|
|
Email: random + "@hodor.hodor",
|
|
IsEmailVerified: true,
|
|
},
|
|
PasswordChangeRequired: false,
|
|
Password: "Password1!",
|
|
})
|
|
}
|
|
|
|
func checkDiscoveryEndpoint(t *testing.T, domain string, containsUILocales, notContainsUILocales []string) {
|
|
resp, err := http.Get("http://" + domain + ":8080/.well-known/openid-configuration")
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
body, err := io.ReadAll(resp.Body)
|
|
defer func() {
|
|
require.NoError(t, resp.Body.Close())
|
|
}()
|
|
require.NoError(t, err)
|
|
doc := struct {
|
|
UILocalesSupported []string `json:"ui_locales_supported"`
|
|
}{}
|
|
require.NoError(t, json.Unmarshal(body, &doc))
|
|
if containsUILocales != nil {
|
|
assert.Condition(NoopAssertionT, contains(doc.UILocalesSupported, containsUILocales))
|
|
}
|
|
if notContainsUILocales != nil {
|
|
assert.Condition(NoopAssertionT, not(contains(doc.UILocalesSupported, notContainsUILocales)))
|
|
}
|
|
}
|
|
|
|
func checkLoginUILanguage(t *testing.T, domain string, acceptLanguage language.Tag, expectLang language.Tag, containsText string) {
|
|
req, err := http.NewRequest(http.MethodGet, "http://"+domain+":8080/ui/login/register", nil)
|
|
req.Header.Set("Accept-Language", acceptLanguage.String())
|
|
require.NoError(t, err)
|
|
resp, err := http.DefaultClient.Do(req)
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
body, err := io.ReadAll(resp.Body)
|
|
defer func() {
|
|
require.NoError(t, resp.Body.Close())
|
|
}()
|
|
require.NoError(t, err)
|
|
assert.Containsf(t, string(body), containsText, "login ui language is in "+expectLang.String())
|
|
}
|
|
|
|
// We would love to use assert.Contains here, but it doesn't work with slices of strings
|
|
func contains(container []string, subset []string) assert.Comparison {
|
|
return func() bool {
|
|
if subset == nil {
|
|
return true
|
|
}
|
|
for _, str := range subset {
|
|
var found bool
|
|
for _, containerStr := range container {
|
|
if str == containerStr {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
func not(cmp assert.Comparison) assert.Comparison {
|
|
return func() bool {
|
|
return !cmp()
|
|
}
|
|
}
|