diff --git a/pkg/extension/service/extension.go b/pkg/extension/service/extension.go
index 995c343467fef3a9b1963ef5d65a1c230ad4c69d..45ed0524f855f888d7baf9befb9080392185dfc1 100644
--- a/pkg/extension/service/extension.go
+++ b/pkg/extension/service/extension.go
@@ -133,22 +133,38 @@ func (s *Extension) setupExtensionClient(set *setup.Setup, spaceID string) {
 	set.AddClient(&client, setup.OverwriteClient(), setup.DeleteClientIfRemove())
 }
 
-func (s *Extension) GetSetup(spaceID, envID string) *setup.Setup {
+func (s *Extension) GetSetup(spaceID, envID string) (*setup.Setup, error) {
 	set := s.setupFunc(spaceID, envID)
+	if set.HasErrors() {
+		s.Logger.Error("Invalid setup config", zap.Errors("Errors", set.Errors()))
+		return nil, set.Error()
+	}
 	s.setupExtensionClient(set, spaceID)
-	return set
+	return set, nil
 }
 
 func (s *Extension) Install(ctx context.Context, in *extension.InstallRequest) error {
-	return s.GetSetup(in.SpaceId, in.EnvId).WithForce(in.Force).Install(ctx)
+	set, err := s.GetSetup(in.SpaceId, in.EnvId)
+	if err != nil {
+		return err
+	}
+	return set.WithForce(in.Force).Install(ctx)
 }
 
 func (s *Extension) Check(ctx context.Context, in *extension.CheckRequest) error {
-	return s.GetSetup(in.SpaceId, in.EnvId).Check(ctx)
+	set, err := s.GetSetup(in.SpaceId, in.EnvId)
+	if err != nil {
+		return err
+	}
+	return set.Check(ctx)
 }
 
 func (s *Extension) Uninstall(ctx context.Context, in *extension.UninstallRequest) error {
-	return s.GetSetup(in.SpaceId, in.EnvId).WithForce(in.Force).WithRemove(in.Remove).Uninstall(ctx)
+	set, err := s.GetSetup(in.SpaceId, in.EnvId)
+	if err != nil {
+		return err
+	}
+	return set.WithForce(in.Force).WithRemove(in.Remove).Uninstall(ctx)
 }
 
 // isCorrectExtension проверяет что расширение в url совпадает с расширением расширения
diff --git a/pkg/permission/permission.go b/pkg/permission/permission.go
index 7a90e5079da08648cbdefe565d39f4d8b801a275..e08579e9c074470a80b8e2ecce3c1e62634b02f1 100644
--- a/pkg/permission/permission.go
+++ b/pkg/permission/permission.go
@@ -53,12 +53,6 @@ func (p Permission) RemoveFields(in map[string]interface{}) map[string]interface
 	if in == nil {
 		return nil
 	}
-	out := make(map[string]interface{})
-	for k, v := range in {
-		if data.Contains(k, p.UnallowedFields) {
-			continue
-		}
-		out[k] = v
-	}
-	return out
+	data.DeleteMany(p.UnallowedFields, in)
+	return in
 }
diff --git a/pkg/permission/permission_test.go b/pkg/permission/permission_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..2a365601e201a6fd31a34b89bcc47d3de14b4406
--- /dev/null
+++ b/pkg/permission/permission_test.go
@@ -0,0 +1,60 @@
+package permission
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestPermission_RemoveFields(t *testing.T) {
+
+	tests := []struct {
+		name            string
+		unallowedFields []string
+		in              map[string]interface{}
+		want            map[string]interface{}
+	}{
+		{
+			name: "nil",
+			in:   nil,
+			want: nil,
+		},
+		{
+			name: "empty",
+			in:   map[string]interface{}{},
+			want: map[string]interface{}{},
+		},
+		{
+			name:            "empty unallowedFields",
+			in:              map[string]interface{}{"f": "v"},
+			want:            map[string]interface{}{"f": "v"},
+			unallowedFields: nil,
+		},
+		{
+			name:            "remove fields",
+			in:              map[string]interface{}{"f": "v", "f1": "v"},
+			want:            map[string]interface{}{"f": "v"},
+			unallowedFields: []string{"f1"},
+		},
+		{
+			name:            "all present fields allowed",
+			in:              map[string]interface{}{"f": "v", "f1": "v"},
+			want:            map[string]interface{}{"f": "v", "f1": "v"},
+			unallowedFields: []string{"f2"},
+		},
+		{
+			name:            "unallowed fields in nested object",
+			in:              map[string]interface{}{"obj": map[string]interface{}{"f": "v", "f1": "v"}},
+			want:            map[string]interface{}{"obj": map[string]interface{}{"f": "v"}},
+			unallowedFields: []string{"obj.f1"},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			p := Permission{
+				UnallowedFields: tt.unallowedFields,
+			}
+			assert.Equal(t, tt.want, p.RemoveFields(tt.in))
+		})
+	}
+}
diff --git a/pkg/permission/ruleset.go b/pkg/permission/ruleset.go
index b9a84f3747223cf4139c905e3d65ebb9f4dea336..0843afc79922be9a20c06009d4084a281b3cd68f 100644
--- a/pkg/permission/ruleset.go
+++ b/pkg/permission/ruleset.go
@@ -133,6 +133,8 @@ func (r Rule) GetPermission(action Action) *Permission {
 				p.UnallowedFields = append(p.UnallowedFields, r.ReadonlyFields...)
 			}
 
+			p.UnallowedFields = data.SetFromSlice(p.UnallowedFields)
+
 			return p
 		}
 	}
diff --git a/pkg/permission/ruleset_test.go b/pkg/permission/ruleset_test.go
index 47c1f07614b1d2edef196db6238adaf40b33e073..175c36261152afcf2b2c585382588f97b5465591 100644
--- a/pkg/permission/ruleset_test.go
+++ b/pkg/permission/ruleset_test.go
@@ -49,3 +49,44 @@ func TestMerge(t *testing.T) {
 		})
 	}
 }
+
+func TestRule_GetPermission(t *testing.T) {
+	tests := []struct {
+		name            string
+		action          Action
+		rule            Rule
+		unallowedFields []string
+		want            *Permission
+	}{
+		{
+			name:            "ActionRead",
+			action:          ActionRead,
+			rule:            Rule{Actions: []Action{ActionRead, ActionUpdate}, ReadonlyFields: []string{"f1"}, HiddenFields: []string{"f2"}, WriteonlyFields: []string{"f3"}},
+			unallowedFields: []string{"f2", "f3"},
+		},
+		{
+			name:            "ActionRead readonly&writeonly",
+			action:          ActionRead,
+			rule:            Rule{Actions: []Action{ActionRead, ActionUpdate}, ReadonlyFields: []string{"f1"}, HiddenFields: []string{"f2"}, WriteonlyFields: []string{"f1"}},
+			unallowedFields: []string{"f1", "f2"},
+		},
+		{
+			name:            "ActionUpdate",
+			action:          ActionUpdate,
+			rule:            Rule{Actions: []Action{ActionRead, ActionUpdate}, ReadonlyFields: []string{"f1"}, HiddenFields: []string{"f2"}, WriteonlyFields: []string{"f3"}},
+			unallowedFields: []string{"f1"},
+		},
+		{
+			name:            "ActionUpdate readonly&writeonly",
+			action:          ActionUpdate,
+			rule:            Rule{Actions: []Action{ActionRead, ActionUpdate}, ReadonlyFields: []string{"f1"}, HiddenFields: []string{"f2"}, WriteonlyFields: []string{"f1"}},
+			unallowedFields: []string{"f1"},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			p := tt.rule.GetPermission(tt.action)
+			assert.ElementsMatch(t, tt.unallowedFields, p.UnallowedFields)
+		})
+	}
+}
diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go
index 6b26f3367a3774f9a29ae18b118cce2dd1a4bd89..e45298241602e6855c63019fe5bebcc4e2df27ee 100644
--- a/pkg/schema/schema.go
+++ b/pkg/schema/schema.go
@@ -71,6 +71,15 @@ func (s Schema) SetMetadata(md map[string]string) *Schema {
 	return &s
 }
 
+func (s *Schema) ConvertTypes() error {
+	b, err := s.MarshalJSON()
+	if err != nil {
+		return errors.Wrap(err, "marshal schema")
+	}
+	*s = *New()
+	return errors.Wrap(s.UnmarshalJSON(b), "unmarshal schema")
+}
+
 func (s *Schema) Load(ctx context.Context) error {
 	if s.Loaded {
 		return nil
diff --git a/pkg/setup/collection.go b/pkg/setup/collection.go
index 725a04e390f3b298e612875378f33a62138cce3f..0a7fc65c9da358240980638e82915089b13e5829 100644
--- a/pkg/setup/collection.go
+++ b/pkg/setup/collection.go
@@ -27,8 +27,19 @@ type CollectionConfig struct {
 	SkipMigration bool
 }
 
-func NewCollectionConfig(collection *collections.Collection, opt ...CollectionsOption) CollectionConfig {
-	c := CollectionConfig{collection: collection}
+func NewCollectionConfig(collection *collections.Collection, opt ...CollectionsOption) (c CollectionConfig, err error) {
+	collection = collection.Clone()
+
+	if collection.Schema != nil {
+		// приведение внутренних типов схемы, чтобы избежать возможного несоответствия типов при
+		// сравнивании схем (`[]interface{}/[]string`, `int/int64`, etc.)
+		err = collection.Schema.ConvertTypes()
+		if err != nil {
+			return
+		}
+	}
+
+	c = CollectionConfig{collection: collection}
 
 	DefaultUpdateCollectionStrategy()(&c)
 	DeleteCollectionIfRemove()(&c)
@@ -37,7 +48,7 @@ func NewCollectionConfig(collection *collections.Collection, opt ...CollectionsO
 		o(&c)
 	}
 
-	return c
+	return c, nil
 }
 
 func SkipMigration() CollectionsOption {
diff --git a/pkg/setup/setup.go b/pkg/setup/setup.go
index 6360eadee88a9cc2345e140f7acf5829e0fc9f26..6101e1db13f40027706977dad4b710261d82439d 100644
--- a/pkg/setup/setup.go
+++ b/pkg/setup/setup.go
@@ -2,6 +2,7 @@ package setup
 
 import (
 	"context"
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
 
 	"git.perx.ru/perxis/perxis-go/pkg/clients"
 	"git.perx.ru/perxis/perxis-go/pkg/collections"
@@ -11,6 +12,10 @@ import (
 	"go.uber.org/zap"
 )
 
+var (
+	ErrInvalidSetupConfig = errors.New("invalid setup config")
+)
+
 // Setup реализует процесс настройки пространства. Указав необходимые требования к конфигурации пространства можно
 // выполнить процесс установки, проверки и удаления требований.
 type Setup struct {
@@ -32,12 +37,12 @@ type Setup struct {
 }
 
 func NewSetup(content *content.Content, spaceID, environmentID string, logger *zap.Logger) *Setup {
-	//logger = logger.With(zap.String("Space", spaceID), zap.String("Environment", environmentID))
-
 	if logger == nil {
 		logger = zap.NewNop()
 	}
 
+	logger = logger.With(zap.String("Space", spaceID), zap.String("Environment", environmentID))
+
 	return &Setup{
 		SpaceID:       spaceID,
 		EnvironmentID: environmentID,
@@ -74,6 +79,14 @@ func (s *Setup) AddError(err error) {
 	s.errors = append(s.errors, err)
 }
 
+func (s *Setup) Errors() []error {
+	return s.errors
+}
+
+func (s *Setup) Error() error {
+	return errors.WithErrors(ErrInvalidSetupConfig, s.errors...)
+}
+
 // AddRoles добавляет требования к настройке ролей в пространстве
 func (s *Setup) AddRoles(roles []*roles.Role, opt ...RolesOption) *Setup {
 	for _, role := range roles {
@@ -109,7 +122,12 @@ func (s *Setup) AddCollections(collections []*collections.Collection, opt ...Col
 }
 
 func (s *Setup) AddCollection(collection *collections.Collection, opt ...CollectionsOption) *Setup {
-	s.Collections = append(s.Collections, NewCollectionConfig(collection, opt...))
+	config, err := NewCollectionConfig(collection, opt...)
+	if err != nil {
+		s.AddError(err)
+		return s
+	}
+	s.Collections = append(s.Collections, config)
 	return s
 }
 
@@ -128,8 +146,6 @@ func (s *Setup) AddItem(item *items.Item, opt ...ItemsOption) *Setup {
 
 // Install выполняет установку необходимых требований
 func (s *Setup) Install(ctx context.Context) error {
-	s.logger = s.logger.With(zap.String("Space", s.SpaceID), zap.String("Environment", s.EnvironmentID))
-
 	if err := s.InstallRoles(ctx); err != nil {
 		return err
 	}
@@ -142,14 +158,11 @@ func (s *Setup) Install(ctx context.Context) error {
 	if err := s.InstallItems(ctx); err != nil {
 		return err
 	}
-
 	return nil
 }
 
 // Check выполняет проверку требований
 func (s *Setup) Check(ctx context.Context) error {
-	s.logger = s.logger.With(zap.String("Space", s.SpaceID), zap.String("Environment", s.EnvironmentID))
-
 	if err := s.CheckRoles(ctx); err != nil {
 		return err
 	}
@@ -162,14 +175,11 @@ func (s *Setup) Check(ctx context.Context) error {
 	if err := s.CheckItems(ctx); err != nil {
 		return err
 	}
-
 	return nil
 }
 
 // Uninstall выполняет удаление установленных раннее требований
 func (s *Setup) Uninstall(ctx context.Context) error {
-	s.logger = s.logger.With(zap.String("Space", s.SpaceID), zap.String("Environment", s.EnvironmentID))
-
 	// В случае если необходимо удалить данные удаляем все что создано при установке расширения
 	if err := s.UninstallClients(ctx); err != nil {
 		return err
@@ -183,6 +193,5 @@ func (s *Setup) Uninstall(ctx context.Context) error {
 	if err := s.UninstallItems(ctx); err != nil {
 		return err
 	}
-
 	return nil
 }