mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
[MM-57295] Bulk export: add roles and permission schemes (#26523)
* Bulk export: add roles and permission schemes * Update mmctl docs * Fix log * Update mmctl tests * Update mmctl unit tests * Refactor to avoid extra calls * Update translations * Add test case * Fix test * Fix test
This commit is contained in:
parent
c7da6b4741
commit
4d6602aff0
@ -90,6 +90,12 @@ func (a *App) BulkExport(ctx request.CTX, writer io.Writer, outPath string, job
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.IncludeRolesAndSchemes {
|
||||||
|
if err := a.exportRolesAndSchemes(ctx, job, writer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx.Logger().Info("Bulk export: exporting teams")
|
ctx.Logger().Info("Bulk export: exporting teams")
|
||||||
teamNames, err := a.exportAllTeams(ctx, job, writer)
|
teamNames, err := a.exportAllTeams(ctx, job, writer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -194,6 +200,106 @@ func (a *App) exportVersion(writer io.Writer) *model.AppError {
|
|||||||
return a.exportWriteLine(writer, versionLine)
|
return a.exportWriteLine(writer, versionLine)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) exportRolesAndSchemes(ctx request.CTX, job *model.Job, writer io.Writer) *model.AppError {
|
||||||
|
// We export schemes first since they'll already include their attached roles
|
||||||
|
// which we map to avoid exporting them twice later in exportRoles.
|
||||||
|
schemeRolesMap := make(map[string]bool)
|
||||||
|
|
||||||
|
roles, appErr := a.Srv().Store().Role().GetAll()
|
||||||
|
if appErr != nil {
|
||||||
|
return model.NewAppError("exportRolesAndSchemes", "app.role.get_all.app_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Logger().Info("Bulk export: exporting team schemes")
|
||||||
|
if err := a.exportSchemes(ctx, job, writer, model.SchemeScopeTeam, schemeRolesMap, roles); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Logger().Info("Bulk export: exporting channel schemes")
|
||||||
|
if err := a.exportSchemes(ctx, job, writer, model.SchemeScopeChannel, schemeRolesMap, roles); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Logger().Info("Bulk export: exporting roles")
|
||||||
|
if err := a.exportRoles(ctx, job, writer, schemeRolesMap, roles); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) exportRoles(ctx request.CTX, job *model.Job, writer io.Writer, schemeRoles map[string]bool, allRoles []*model.Role) *model.AppError {
|
||||||
|
var cnt int
|
||||||
|
for _, role := range allRoles {
|
||||||
|
// We skip any roles that will be included as part of custom schemes.
|
||||||
|
if !schemeRoles[role.Name] {
|
||||||
|
if err := a.exportWriteLine(writer, ImportLineFromRole(role)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cnt++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateJobProgress(ctx.Logger(), a.Srv().Store(), job, "roles_exported", cnt)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) exportSchemes(ctx request.CTX, job *model.Job, writer io.Writer, scope string, schemeRolesMap map[string]bool, allRoles []*model.Role) *model.AppError {
|
||||||
|
rolesMap := make(map[string]*model.Role, len(allRoles))
|
||||||
|
for _, role := range allRoles {
|
||||||
|
rolesMap[role.Name] = role
|
||||||
|
}
|
||||||
|
|
||||||
|
var cnt int
|
||||||
|
pageSize := 100
|
||||||
|
|
||||||
|
for {
|
||||||
|
schemes, err := a.Srv().Store().Scheme().GetAllPage(scope, cnt, pageSize)
|
||||||
|
if err != nil {
|
||||||
|
return model.NewAppError("exportSchemes", "app.scheme.get_all_page.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, scheme := range schemes {
|
||||||
|
if ok := scheme.IsValid(); !ok {
|
||||||
|
return model.NewAppError("exportSchemes", "model.scheme.is_valid.app_error", nil, "", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
if scheme.Scope == model.SchemeScopeTeam {
|
||||||
|
schemeRolesMap[scheme.DefaultTeamAdminRole] = true
|
||||||
|
schemeRolesMap[scheme.DefaultTeamUserRole] = true
|
||||||
|
schemeRolesMap[scheme.DefaultTeamGuestRole] = true
|
||||||
|
|
||||||
|
// Playbooks
|
||||||
|
// At the moment this is only needed to avoid exporting and
|
||||||
|
// importing spurious roles.
|
||||||
|
schemeRolesMap[scheme.DefaultPlaybookAdminRole] = true
|
||||||
|
schemeRolesMap[scheme.DefaultPlaybookMemberRole] = true
|
||||||
|
schemeRolesMap[scheme.DefaultRunAdminRole] = true
|
||||||
|
schemeRolesMap[scheme.DefaultRunMemberRole] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if scheme.Scope == model.SchemeScopeTeam || scheme.Scope == model.SchemeScopeChannel {
|
||||||
|
schemeRolesMap[scheme.DefaultChannelAdminRole] = true
|
||||||
|
schemeRolesMap[scheme.DefaultChannelUserRole] = true
|
||||||
|
schemeRolesMap[scheme.DefaultChannelGuestRole] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.exportWriteLine(writer, ImportLineFromScheme(scheme, rolesMap)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt += len(schemes)
|
||||||
|
|
||||||
|
updateJobProgress(ctx.Logger(), a.Srv().Store(), job, fmt.Sprintf("%s_schemes_exported", scope), cnt)
|
||||||
|
|
||||||
|
if len(schemes) < pageSize {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) exportAllTeams(ctx request.CTX, job *model.Job, writer io.Writer) (map[string]bool, *model.AppError) {
|
func (a *App) exportAllTeams(ctx request.CTX, job *model.Job, writer io.Writer) (map[string]bool, *model.AppError) {
|
||||||
afterId := strings.Repeat("0", 26)
|
afterId := strings.Repeat("0", 26)
|
||||||
teamNames := make(map[string]bool)
|
teamNames := make(map[string]bool)
|
||||||
|
@ -226,3 +226,46 @@ func ImportLineFromEmoji(emoji *model.Emoji, filePath string) *imports.LineImpor
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ImportRoleDataFromRole(role *model.Role) *imports.RoleImportData {
|
||||||
|
return &imports.RoleImportData{
|
||||||
|
Name: &role.Name,
|
||||||
|
DisplayName: &role.DisplayName,
|
||||||
|
Description: &role.Description,
|
||||||
|
Permissions: &role.Permissions,
|
||||||
|
SchemeManaged: &role.SchemeManaged,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ImportLineFromRole(role *model.Role) *imports.LineImportData {
|
||||||
|
return &imports.LineImportData{
|
||||||
|
Type: "role",
|
||||||
|
Role: ImportRoleDataFromRole(role),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ImportLineFromScheme(scheme *model.Scheme, rolesMap map[string]*model.Role) *imports.LineImportData {
|
||||||
|
data := &imports.SchemeImportData{
|
||||||
|
Name: &scheme.Name,
|
||||||
|
DisplayName: &scheme.DisplayName,
|
||||||
|
Description: &scheme.Description,
|
||||||
|
Scope: &scheme.Scope,
|
||||||
|
}
|
||||||
|
|
||||||
|
if scheme.Scope == model.SchemeScopeTeam {
|
||||||
|
data.DefaultTeamAdminRole = ImportRoleDataFromRole(rolesMap[scheme.DefaultTeamAdminRole])
|
||||||
|
data.DefaultTeamUserRole = ImportRoleDataFromRole(rolesMap[scheme.DefaultTeamUserRole])
|
||||||
|
data.DefaultTeamGuestRole = ImportRoleDataFromRole(rolesMap[scheme.DefaultTeamGuestRole])
|
||||||
|
}
|
||||||
|
|
||||||
|
if scheme.Scope == model.SchemeScopeTeam || scheme.Scope == model.SchemeScopeChannel {
|
||||||
|
data.DefaultChannelAdminRole = ImportRoleDataFromRole(rolesMap[scheme.DefaultChannelAdminRole])
|
||||||
|
data.DefaultChannelUserRole = ImportRoleDataFromRole(rolesMap[scheme.DefaultChannelUserRole])
|
||||||
|
data.DefaultChannelGuestRole = ImportRoleDataFromRole(rolesMap[scheme.DefaultChannelGuestRole])
|
||||||
|
}
|
||||||
|
|
||||||
|
return &imports.LineImportData{
|
||||||
|
Type: "scheme",
|
||||||
|
Scheme: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -804,3 +804,412 @@ func TestExportArchivedChannels(t *testing.T) {
|
|||||||
}
|
}
|
||||||
require.True(t, found, "archived channel not found after import")
|
require.True(t, found, "archived channel not found after import")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExportRoles(t *testing.T) {
|
||||||
|
t.Run("defaults", func(t *testing.T) {
|
||||||
|
th1 := Setup(t).InitBasic()
|
||||||
|
defer th1.TearDown()
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
appErr := th1.App.BulkExport(th1.Context, &b, "", nil, model.BulkExportOpts{})
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
exportedRoles, appErr := th1.App.GetAllRoles()
|
||||||
|
assert.Nil(t, appErr)
|
||||||
|
assert.NotEmpty(t, exportedRoles)
|
||||||
|
|
||||||
|
th2 := Setup(t)
|
||||||
|
defer th2.TearDown()
|
||||||
|
appErr, i := th2.App.BulkImport(th2.Context, &b, nil, false, 1)
|
||||||
|
assert.Nil(t, appErr)
|
||||||
|
assert.Equal(t, 0, i)
|
||||||
|
|
||||||
|
importedRoles, appErr := th2.App.GetAllRoles()
|
||||||
|
assert.Nil(t, appErr)
|
||||||
|
assert.NotEmpty(t, importedRoles)
|
||||||
|
|
||||||
|
require.Equal(t, len(exportedRoles), len(importedRoles))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("modified roles", func(t *testing.T) {
|
||||||
|
th1 := Setup(t).InitBasic()
|
||||||
|
defer th1.TearDown()
|
||||||
|
|
||||||
|
exportedRole, appErr := th1.App.GetRoleByName(th1.Context.Context(), model.TeamUserRoleId)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
exportedRole.Permissions = exportedRole.Permissions[1:]
|
||||||
|
|
||||||
|
_, appErr = th1.App.UpdateRole(exportedRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
appErr = th1.App.BulkExport(th1.Context, &b, "", nil, model.BulkExportOpts{
|
||||||
|
IncludeRolesAndSchemes: true,
|
||||||
|
})
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
th2 := Setup(t)
|
||||||
|
defer th2.TearDown()
|
||||||
|
appErr, i := th2.App.BulkImport(th2.Context, &b, nil, false, 1)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, 0, i)
|
||||||
|
|
||||||
|
importedRole, appErr := th2.App.GetRoleByName(th2.Context.Context(), model.TeamUserRoleId)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
require.Equal(t, exportedRole.DisplayName, importedRole.DisplayName)
|
||||||
|
require.Equal(t, exportedRole.Description, importedRole.Description)
|
||||||
|
require.Equal(t, exportedRole.SchemeManaged, importedRole.SchemeManaged)
|
||||||
|
require.Equal(t, exportedRole.BuiltIn, importedRole.BuiltIn)
|
||||||
|
require.ElementsMatch(t, exportedRole.Permissions, importedRole.Permissions)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("custom roles", func(t *testing.T) {
|
||||||
|
th1 := Setup(t).InitBasic()
|
||||||
|
defer th1.TearDown()
|
||||||
|
|
||||||
|
exportedRoles, appErr := th1.App.GetAllRoles()
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.NotEmpty(t, exportedRoles)
|
||||||
|
|
||||||
|
customRole, appErr := th1.App.CreateRole(&model.Role{
|
||||||
|
Name: "custom_role",
|
||||||
|
DisplayName: "custom_role",
|
||||||
|
Permissions: exportedRoles[0].Permissions,
|
||||||
|
})
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
appErr = th1.App.BulkExport(th1.Context, &b, "", nil, model.BulkExportOpts{
|
||||||
|
IncludeRolesAndSchemes: true,
|
||||||
|
})
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
th2 := Setup(t)
|
||||||
|
defer th2.TearDown()
|
||||||
|
appErr, i := th2.App.BulkImport(th2.Context, &b, nil, false, 1)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, 0, i)
|
||||||
|
|
||||||
|
importedCustomRole, appErr := th2.App.GetRoleByName(th2.Context.Context(), customRole.Name)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
require.Equal(t, customRole.DisplayName, importedCustomRole.DisplayName)
|
||||||
|
require.Equal(t, customRole.Description, importedCustomRole.Description)
|
||||||
|
require.Equal(t, customRole.SchemeManaged, importedCustomRole.SchemeManaged)
|
||||||
|
require.Equal(t, customRole.BuiltIn, importedCustomRole.BuiltIn)
|
||||||
|
require.ElementsMatch(t, customRole.Permissions, importedCustomRole.Permissions)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExportSchemes(t *testing.T) {
|
||||||
|
t.Run("no schemes", func(t *testing.T) {
|
||||||
|
th1 := Setup(t).InitBasic()
|
||||||
|
defer th1.TearDown()
|
||||||
|
|
||||||
|
// Need to set this or working with schemes won't work until the job is
|
||||||
|
// completed which is unnecessary for the purpose of this test.
|
||||||
|
err := th1.App.Srv().Store().System().Save(&model.System{Name: model.MigrationKeyAdvancedPermissionsPhase2, Value: "true"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
schemes, err := th1.App.Srv().Store().Scheme().GetAllPage(model.SchemeScopeChannel, 0, 1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Empty(t, schemes)
|
||||||
|
|
||||||
|
schemes, err = th1.App.Srv().Store().Scheme().GetAllPage(model.SchemeScopeTeam, 0, 1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Empty(t, schemes)
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
appErr := th1.App.BulkExport(th1.Context, &b, "", nil, model.BulkExportOpts{
|
||||||
|
IncludeRolesAndSchemes: true,
|
||||||
|
})
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
// The following causes the original store to be wiped so from here on we are targeting the
|
||||||
|
// second instance where the import will be loaded.
|
||||||
|
th2 := Setup(t)
|
||||||
|
defer th2.TearDown()
|
||||||
|
err = th2.App.Srv().Store().System().Save(&model.System{Name: model.MigrationKeyAdvancedPermissionsPhase2, Value: "true"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
appErr, i := th2.App.BulkImport(th2.Context, &b, nil, false, 1)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, 0, i)
|
||||||
|
|
||||||
|
schemes, err = th2.App.Srv().Store().Scheme().GetAllPage(model.SchemeScopeChannel, 0, 1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Empty(t, schemes)
|
||||||
|
|
||||||
|
schemes, err = th2.App.Srv().Store().Scheme().GetAllPage(model.SchemeScopeTeam, 0, 1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Empty(t, schemes)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("skip export", func(t *testing.T) {
|
||||||
|
th1 := Setup(t).InitBasic()
|
||||||
|
defer th1.TearDown()
|
||||||
|
|
||||||
|
// Need to set this or working with schemes won't work until the job is
|
||||||
|
// completed which is unnecessary for the purpose of this test.
|
||||||
|
err := th1.App.Srv().Store().System().Save(&model.System{Name: model.MigrationKeyAdvancedPermissionsPhase2, Value: "true"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
customScheme, appErr := th1.App.CreateScheme(&model.Scheme{
|
||||||
|
Name: "custom_scheme",
|
||||||
|
DisplayName: "Custom Scheme",
|
||||||
|
Scope: model.SchemeScopeChannel,
|
||||||
|
})
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
appErr = th1.App.BulkExport(th1.Context, &b, "", nil, model.BulkExportOpts{})
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
// The following causes the original store to be wiped so from here on we are targeting the
|
||||||
|
// second instance where the import will be loaded.
|
||||||
|
th2 := Setup(t)
|
||||||
|
defer th2.TearDown()
|
||||||
|
err = th2.App.Srv().Store().System().Save(&model.System{Name: model.MigrationKeyAdvancedPermissionsPhase2, Value: "true"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
appErr, i := th2.App.BulkImport(th2.Context, &b, nil, false, 1)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, 0, i)
|
||||||
|
|
||||||
|
// Verify the scheme doesn't exist which is the expectation as it wasn't exported.
|
||||||
|
_, appErr = th2.App.GetScheme(customScheme.Name)
|
||||||
|
require.NotNil(t, appErr)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("export channel scheme", func(t *testing.T) {
|
||||||
|
th1 := Setup(t).InitBasic()
|
||||||
|
defer th1.TearDown()
|
||||||
|
|
||||||
|
// Need to set this or working with schemes won't work until the job is
|
||||||
|
// completed which is unnecessary for the purpose of this test.
|
||||||
|
err := th1.App.Srv().Store().System().Save(&model.System{Name: model.MigrationKeyAdvancedPermissionsPhase2, Value: "true"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
builtInRoles := 23
|
||||||
|
defaultChannelSchemeRoles := 3
|
||||||
|
|
||||||
|
// Verify the roles count is expected prior to scheme creation.
|
||||||
|
roles, appErr := th1.App.GetAllRoles()
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Len(t, roles, builtInRoles)
|
||||||
|
|
||||||
|
customScheme, appErr := th1.App.CreateScheme(&model.Scheme{
|
||||||
|
Name: "custom_channel_scheme",
|
||||||
|
DisplayName: "Custom Channel Scheme",
|
||||||
|
Scope: model.SchemeScopeChannel,
|
||||||
|
})
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
// Verify the roles count is expected after scheme creation.
|
||||||
|
roles, appErr = th1.App.GetAllRoles()
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Len(t, roles, builtInRoles+defaultChannelSchemeRoles)
|
||||||
|
|
||||||
|
// Fetch the scheme roles for later comparison
|
||||||
|
customChannelAdminRole, appErr := th1.App.GetRoleByName(th1.Context.Context(), customScheme.DefaultChannelAdminRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
customChannelUserRole, appErr := th1.App.GetRoleByName(th1.Context.Context(), customScheme.DefaultChannelUserRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
customChannelGuestRole, appErr := th1.App.GetRoleByName(th1.Context.Context(), customScheme.DefaultChannelGuestRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
appErr = th1.App.BulkExport(th1.Context, &b, "", nil, model.BulkExportOpts{
|
||||||
|
IncludeRolesAndSchemes: true,
|
||||||
|
})
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
// The following causes the original store to be wiped so from here on we are targeting the
|
||||||
|
// second instance where the import will be loaded.
|
||||||
|
th2 := Setup(t)
|
||||||
|
defer th2.TearDown()
|
||||||
|
err = th2.App.Srv().Store().System().Save(&model.System{Name: model.MigrationKeyAdvancedPermissionsPhase2, Value: "true"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify roles count before importing is as expected.
|
||||||
|
roles, appErr = th2.App.GetAllRoles()
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Len(t, roles, builtInRoles)
|
||||||
|
|
||||||
|
appErr, i := th2.App.BulkImport(th2.Context, &b, nil, false, 1)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, 0, i)
|
||||||
|
|
||||||
|
// Verify roles count after importing is as expected.
|
||||||
|
roles, appErr = th2.App.GetAllRoles()
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Len(t, roles, builtInRoles+defaultChannelSchemeRoles)
|
||||||
|
|
||||||
|
// Verify schemes match
|
||||||
|
importedScheme, appErr := th2.App.GetSchemeByName(customScheme.Name)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, customScheme.Name, importedScheme.Name)
|
||||||
|
require.Equal(t, customScheme.DisplayName, importedScheme.DisplayName)
|
||||||
|
require.Equal(t, customScheme.Description, importedScheme.Description)
|
||||||
|
require.Equal(t, customScheme.Scope, importedScheme.Scope)
|
||||||
|
|
||||||
|
// Verify scheme roles match
|
||||||
|
importedChannelAdminRole, appErr := th2.App.GetRoleByName(th2.Context.Context(), importedScheme.DefaultChannelAdminRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, customChannelAdminRole.DisplayName, importedChannelAdminRole.DisplayName)
|
||||||
|
require.Equal(t, customChannelAdminRole.Description, importedChannelAdminRole.Description)
|
||||||
|
require.Equal(t, customChannelAdminRole.Permissions, importedChannelAdminRole.Permissions)
|
||||||
|
require.Equal(t, customChannelAdminRole.SchemeManaged, importedChannelAdminRole.SchemeManaged)
|
||||||
|
require.Equal(t, customChannelAdminRole.BuiltIn, importedChannelAdminRole.BuiltIn)
|
||||||
|
|
||||||
|
importedChannelUserRole, appErr := th2.App.GetRoleByName(th2.Context.Context(), importedScheme.DefaultChannelUserRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, customChannelUserRole.DisplayName, importedChannelUserRole.DisplayName)
|
||||||
|
require.Equal(t, customChannelUserRole.Description, importedChannelUserRole.Description)
|
||||||
|
require.Equal(t, customChannelUserRole.Permissions, importedChannelUserRole.Permissions)
|
||||||
|
require.Equal(t, customChannelUserRole.SchemeManaged, importedChannelUserRole.SchemeManaged)
|
||||||
|
require.Equal(t, customChannelUserRole.BuiltIn, importedChannelUserRole.BuiltIn)
|
||||||
|
|
||||||
|
importedChannelGuestRole, appErr := th2.App.GetRoleByName(th2.Context.Context(), importedScheme.DefaultChannelGuestRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, customChannelGuestRole.DisplayName, importedChannelGuestRole.DisplayName)
|
||||||
|
require.Equal(t, customChannelGuestRole.Description, importedChannelGuestRole.Description)
|
||||||
|
require.Equal(t, customChannelGuestRole.Permissions, importedChannelGuestRole.Permissions)
|
||||||
|
require.Equal(t, customChannelGuestRole.SchemeManaged, importedChannelGuestRole.SchemeManaged)
|
||||||
|
require.Equal(t, customChannelGuestRole.BuiltIn, importedChannelGuestRole.BuiltIn)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("export team scheme", func(t *testing.T) {
|
||||||
|
th1 := Setup(t).InitBasic()
|
||||||
|
defer th1.TearDown()
|
||||||
|
|
||||||
|
// Need to set this or working with schemes won't work until the job is
|
||||||
|
// completed which is unnecessary for the purpose of this test.
|
||||||
|
err := th1.App.Srv().Store().System().Save(&model.System{Name: model.MigrationKeyAdvancedPermissionsPhase2, Value: "true"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
builtInRoles := 23
|
||||||
|
defaultTeamSchemeRoles := 10
|
||||||
|
|
||||||
|
// Verify the roles count is expected prior to scheme creation.
|
||||||
|
roles, appErr := th1.App.GetAllRoles()
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Len(t, roles, builtInRoles)
|
||||||
|
|
||||||
|
customScheme, appErr := th1.App.CreateScheme(&model.Scheme{
|
||||||
|
Name: "custom_team_scheme",
|
||||||
|
DisplayName: "Custom Team Scheme",
|
||||||
|
Scope: model.SchemeScopeTeam,
|
||||||
|
})
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
// Verify the roles count is expected after scheme creation.
|
||||||
|
roles, appErr = th1.App.GetAllRoles()
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Len(t, roles, builtInRoles+defaultTeamSchemeRoles)
|
||||||
|
|
||||||
|
customChannelAdminRole, appErr := th1.App.GetRoleByName(th1.Context.Context(), customScheme.DefaultChannelAdminRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
customChannelUserRole, appErr := th1.App.GetRoleByName(th1.Context.Context(), customScheme.DefaultChannelUserRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
customChannelGuestRole, appErr := th1.App.GetRoleByName(th1.Context.Context(), customScheme.DefaultChannelGuestRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
customTeamAdminRole, appErr := th1.App.GetRoleByName(th1.Context.Context(), customScheme.DefaultTeamAdminRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
customTeamUserRole, appErr := th1.App.GetRoleByName(th1.Context.Context(), customScheme.DefaultTeamUserRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
customTeamGuestRole, appErr := th1.App.GetRoleByName(th1.Context.Context(), customScheme.DefaultTeamGuestRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
appErr = th1.App.BulkExport(th1.Context, &b, "", nil, model.BulkExportOpts{
|
||||||
|
IncludeRolesAndSchemes: true,
|
||||||
|
})
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
// The following causes the original store to be wiped so from here on we are targeting the
|
||||||
|
// second instance where the import will be loaded.
|
||||||
|
th2 := Setup(t)
|
||||||
|
defer th2.TearDown()
|
||||||
|
err = th2.App.Srv().Store().System().Save(&model.System{Name: model.MigrationKeyAdvancedPermissionsPhase2, Value: "true"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify roles count before importing is as expected.
|
||||||
|
roles, appErr = th2.App.GetAllRoles()
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Len(t, roles, builtInRoles)
|
||||||
|
|
||||||
|
appErr, i := th2.App.BulkImport(th2.Context, &b, nil, false, 1)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, 0, i)
|
||||||
|
|
||||||
|
// Verify roles count after importing is as expected.
|
||||||
|
roles, appErr = th2.App.GetAllRoles()
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Len(t, roles, builtInRoles+defaultTeamSchemeRoles)
|
||||||
|
|
||||||
|
// Verify schemes match
|
||||||
|
importedScheme, appErr := th2.App.GetSchemeByName(customScheme.Name)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, customScheme.Name, importedScheme.Name)
|
||||||
|
require.Equal(t, customScheme.DisplayName, importedScheme.DisplayName)
|
||||||
|
require.Equal(t, customScheme.Description, importedScheme.Description)
|
||||||
|
require.Equal(t, customScheme.Scope, importedScheme.Scope)
|
||||||
|
|
||||||
|
// Verify scheme roles match
|
||||||
|
importedChannelAdminRole, appErr := th2.App.GetRoleByName(th2.Context.Context(), importedScheme.DefaultChannelAdminRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, customChannelAdminRole.DisplayName, importedChannelAdminRole.DisplayName)
|
||||||
|
require.Equal(t, customChannelAdminRole.Description, importedChannelAdminRole.Description)
|
||||||
|
require.Equal(t, customChannelAdminRole.Permissions, importedChannelAdminRole.Permissions)
|
||||||
|
require.Equal(t, customChannelAdminRole.SchemeManaged, importedChannelAdminRole.SchemeManaged)
|
||||||
|
require.Equal(t, customChannelAdminRole.BuiltIn, importedChannelAdminRole.BuiltIn)
|
||||||
|
|
||||||
|
importedChannelUserRole, appErr := th2.App.GetRoleByName(th2.Context.Context(), importedScheme.DefaultChannelUserRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, customChannelUserRole.DisplayName, importedChannelUserRole.DisplayName)
|
||||||
|
require.Equal(t, customChannelUserRole.Description, importedChannelUserRole.Description)
|
||||||
|
require.Equal(t, customChannelUserRole.Permissions, importedChannelUserRole.Permissions)
|
||||||
|
require.Equal(t, customChannelUserRole.SchemeManaged, importedChannelUserRole.SchemeManaged)
|
||||||
|
require.Equal(t, customChannelUserRole.BuiltIn, importedChannelUserRole.BuiltIn)
|
||||||
|
|
||||||
|
importedChannelGuestRole, appErr := th2.App.GetRoleByName(th2.Context.Context(), importedScheme.DefaultChannelGuestRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, customChannelGuestRole.DisplayName, importedChannelGuestRole.DisplayName)
|
||||||
|
require.Equal(t, customChannelGuestRole.Description, importedChannelGuestRole.Description)
|
||||||
|
require.Equal(t, customChannelGuestRole.Permissions, importedChannelGuestRole.Permissions)
|
||||||
|
require.Equal(t, customChannelGuestRole.SchemeManaged, importedChannelGuestRole.SchemeManaged)
|
||||||
|
require.Equal(t, customChannelGuestRole.BuiltIn, importedChannelGuestRole.BuiltIn)
|
||||||
|
|
||||||
|
importedTeamAdminRole, appErr := th2.App.GetRoleByName(th2.Context.Context(), importedScheme.DefaultTeamAdminRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, customTeamAdminRole.DisplayName, importedTeamAdminRole.DisplayName)
|
||||||
|
require.Equal(t, customTeamAdminRole.Description, importedTeamAdminRole.Description)
|
||||||
|
require.Equal(t, customTeamAdminRole.Permissions, importedTeamAdminRole.Permissions)
|
||||||
|
require.Equal(t, customTeamAdminRole.SchemeManaged, importedTeamAdminRole.SchemeManaged)
|
||||||
|
require.Equal(t, customTeamAdminRole.BuiltIn, importedTeamAdminRole.BuiltIn)
|
||||||
|
|
||||||
|
importedTeamUserRole, appErr := th2.App.GetRoleByName(th2.Context.Context(), importedScheme.DefaultTeamUserRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, customTeamUserRole.DisplayName, importedTeamUserRole.DisplayName)
|
||||||
|
require.Equal(t, customTeamUserRole.Description, importedTeamUserRole.Description)
|
||||||
|
require.Equal(t, customTeamUserRole.Permissions, importedTeamUserRole.Permissions)
|
||||||
|
require.Equal(t, customTeamUserRole.SchemeManaged, importedTeamUserRole.SchemeManaged)
|
||||||
|
require.Equal(t, customTeamUserRole.BuiltIn, importedTeamUserRole.BuiltIn)
|
||||||
|
|
||||||
|
importedTeamGuestRole, appErr := th2.App.GetRoleByName(th2.Context.Context(), importedScheme.DefaultTeamGuestRole)
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, customTeamGuestRole.DisplayName, importedTeamGuestRole.DisplayName)
|
||||||
|
require.Equal(t, customTeamGuestRole.Description, importedTeamGuestRole.Description)
|
||||||
|
require.Equal(t, customTeamGuestRole.Permissions, importedTeamGuestRole.Permissions)
|
||||||
|
require.Equal(t, customTeamGuestRole.SchemeManaged, importedTeamGuestRole.SchemeManaged)
|
||||||
|
require.Equal(t, customTeamGuestRole.BuiltIn, importedTeamGuestRole.BuiltIn)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -314,6 +314,11 @@ func processImportDataFileVersionLine(line imports.LineImportData) (int, *model.
|
|||||||
|
|
||||||
func (a *App) importLine(c request.CTX, line imports.LineImportData, dryRun bool) *model.AppError {
|
func (a *App) importLine(c request.CTX, line imports.LineImportData, dryRun bool) *model.AppError {
|
||||||
switch {
|
switch {
|
||||||
|
case line.Type == "role":
|
||||||
|
if line.Role == nil {
|
||||||
|
return model.NewAppError("BulkImport", "app.import.import_line.null_role.error", nil, "", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
return a.importRole(c, line.Role, dryRun)
|
||||||
case line.Type == "scheme":
|
case line.Type == "scheme":
|
||||||
if line.Scheme == nil {
|
if line.Scheme == nil {
|
||||||
return model.NewAppError("BulkImport", "app.import.import_line.null_scheme.error", nil, "", http.StatusBadRequest)
|
return model.NewAppError("BulkImport", "app.import.import_line.null_scheme.error", nil, "", http.StatusBadRequest)
|
||||||
|
@ -31,9 +31,9 @@ import (
|
|||||||
func (a *App) importScheme(rctx request.CTX, data *imports.SchemeImportData, dryRun bool) *model.AppError {
|
func (a *App) importScheme(rctx request.CTX, data *imports.SchemeImportData, dryRun bool) *model.AppError {
|
||||||
var fields []mlog.Field
|
var fields []mlog.Field
|
||||||
if data != nil && data.Name != nil {
|
if data != nil && data.Name != nil {
|
||||||
fields = append(fields, mlog.String("schema_name", *data.Name))
|
fields = append(fields, mlog.String("scheme_name", *data.Name))
|
||||||
}
|
}
|
||||||
rctx.Logger().Info("Validating schema", fields...)
|
rctx.Logger().Info("Validating scheme", fields...)
|
||||||
|
|
||||||
if err := imports.ValidateSchemeImportData(data); err != nil {
|
if err := imports.ValidateSchemeImportData(data); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -44,7 +44,7 @@ func (a *App) importScheme(rctx request.CTX, data *imports.SchemeImportData, dry
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rctx.Logger().Info("Importing schema", fields...)
|
rctx.Logger().Info("Importing scheme", fields...)
|
||||||
|
|
||||||
scheme, err := a.GetSchemeByName(*data.Name)
|
scheme, err := a.GetSchemeByName(*data.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -73,44 +73,46 @@ func (a *App) importScheme(rctx request.CTX, data *imports.SchemeImportData, dry
|
|||||||
|
|
||||||
if scheme.Scope == model.SchemeScopeTeam {
|
if scheme.Scope == model.SchemeScopeTeam {
|
||||||
data.DefaultTeamAdminRole.Name = &scheme.DefaultTeamAdminRole
|
data.DefaultTeamAdminRole.Name = &scheme.DefaultTeamAdminRole
|
||||||
if err := a.importRole(rctx, data.DefaultTeamAdminRole, dryRun, true); err != nil {
|
if err := a.importRole(rctx, data.DefaultTeamAdminRole, dryRun); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
data.DefaultTeamUserRole.Name = &scheme.DefaultTeamUserRole
|
data.DefaultTeamUserRole.Name = &scheme.DefaultTeamUserRole
|
||||||
if err := a.importRole(rctx, data.DefaultTeamUserRole, dryRun, true); err != nil {
|
if err := a.importRole(rctx, data.DefaultTeamUserRole, dryRun); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if data.DefaultTeamGuestRole == nil {
|
if data.DefaultTeamGuestRole == nil {
|
||||||
data.DefaultTeamGuestRole = &imports.RoleImportData{
|
data.DefaultTeamGuestRole = &imports.RoleImportData{
|
||||||
DisplayName: model.NewString("Team Guest Role for Scheme"),
|
DisplayName: model.NewString("Team Guest Role for Scheme"),
|
||||||
|
SchemeManaged: model.NewBool(true),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data.DefaultTeamGuestRole.Name = &scheme.DefaultTeamGuestRole
|
data.DefaultTeamGuestRole.Name = &scheme.DefaultTeamGuestRole
|
||||||
if err := a.importRole(rctx, data.DefaultTeamGuestRole, dryRun, true); err != nil {
|
if err := a.importRole(rctx, data.DefaultTeamGuestRole, dryRun); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if scheme.Scope == model.SchemeScopeTeam || scheme.Scope == model.SchemeScopeChannel {
|
if scheme.Scope == model.SchemeScopeTeam || scheme.Scope == model.SchemeScopeChannel {
|
||||||
data.DefaultChannelAdminRole.Name = &scheme.DefaultChannelAdminRole
|
data.DefaultChannelAdminRole.Name = &scheme.DefaultChannelAdminRole
|
||||||
if err := a.importRole(rctx, data.DefaultChannelAdminRole, dryRun, true); err != nil {
|
if err := a.importRole(rctx, data.DefaultChannelAdminRole, dryRun); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
data.DefaultChannelUserRole.Name = &scheme.DefaultChannelUserRole
|
data.DefaultChannelUserRole.Name = &scheme.DefaultChannelUserRole
|
||||||
if err := a.importRole(rctx, data.DefaultChannelUserRole, dryRun, true); err != nil {
|
if err := a.importRole(rctx, data.DefaultChannelUserRole, dryRun); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if data.DefaultChannelGuestRole == nil {
|
if data.DefaultChannelGuestRole == nil {
|
||||||
data.DefaultChannelGuestRole = &imports.RoleImportData{
|
data.DefaultChannelGuestRole = &imports.RoleImportData{
|
||||||
DisplayName: model.NewString("Channel Guest Role for Scheme"),
|
DisplayName: model.NewString("Channel Guest Role for Scheme"),
|
||||||
|
SchemeManaged: model.NewBool(true),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data.DefaultChannelGuestRole.Name = &scheme.DefaultChannelGuestRole
|
data.DefaultChannelGuestRole.Name = &scheme.DefaultChannelGuestRole
|
||||||
if err := a.importRole(rctx, data.DefaultChannelGuestRole, dryRun, true); err != nil {
|
if err := a.importRole(rctx, data.DefaultChannelGuestRole, dryRun); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,18 +120,16 @@ func (a *App) importScheme(rctx request.CTX, data *imports.SchemeImportData, dry
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) importRole(rctx request.CTX, data *imports.RoleImportData, dryRun bool, isSchemeRole bool) *model.AppError {
|
func (a *App) importRole(rctx request.CTX, data *imports.RoleImportData, dryRun bool) *model.AppError {
|
||||||
var fields []mlog.Field
|
var fields []mlog.Field
|
||||||
if data != nil && data.Name != nil {
|
if data != nil && data.Name != nil {
|
||||||
fields = append(fields, mlog.String("role_name", *data.Name))
|
fields = append(fields, mlog.String("role_name", *data.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isSchemeRole {
|
rctx.Logger().Info("Validating role", fields...)
|
||||||
rctx.Logger().Info("Validating role", fields...)
|
|
||||||
|
|
||||||
if err := imports.ValidateRoleImportData(data); err != nil {
|
if err := imports.ValidateRoleImportData(data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a Dry Run, do not continue any further.
|
// If this is a Dry Run, do not continue any further.
|
||||||
@ -158,10 +158,8 @@ func (a *App) importRole(rctx request.CTX, data *imports.RoleImportData, dryRun
|
|||||||
role.Permissions = *data.Permissions
|
role.Permissions = *data.Permissions
|
||||||
}
|
}
|
||||||
|
|
||||||
if isSchemeRole {
|
if data.SchemeManaged != nil {
|
||||||
role.SchemeManaged = true
|
role.SchemeManaged = *data.SchemeManaged
|
||||||
} else {
|
|
||||||
role.SchemeManaged = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if role.Id == "" {
|
if role.Id == "" {
|
||||||
|
@ -413,7 +413,7 @@ func TestImportImportRole(t *testing.T) {
|
|||||||
Name: &rid1,
|
Name: &rid1,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := th.App.importRole(th.Context, &data, true, false)
|
err := th.App.importRole(th.Context, &data, true)
|
||||||
require.NotNil(t, err, "Should have failed to import.")
|
require.NotNil(t, err, "Should have failed to import.")
|
||||||
|
|
||||||
_, nErr := th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
|
_, nErr := th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
|
||||||
@ -422,7 +422,7 @@ func TestImportImportRole(t *testing.T) {
|
|||||||
// Try importing the valid role in dryRun mode.
|
// Try importing the valid role in dryRun mode.
|
||||||
data.DisplayName = ptrStr("display name")
|
data.DisplayName = ptrStr("display name")
|
||||||
|
|
||||||
err = th.App.importRole(th.Context, &data, true, false)
|
err = th.App.importRole(th.Context, &data, true)
|
||||||
require.Nil(t, err, "Should have succeeded.")
|
require.Nil(t, err, "Should have succeeded.")
|
||||||
|
|
||||||
_, nErr = th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
|
_, nErr = th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
|
||||||
@ -431,7 +431,7 @@ func TestImportImportRole(t *testing.T) {
|
|||||||
// Try importing an invalid role.
|
// Try importing an invalid role.
|
||||||
data.DisplayName = nil
|
data.DisplayName = nil
|
||||||
|
|
||||||
err = th.App.importRole(th.Context, &data, false, false)
|
err = th.App.importRole(th.Context, &data, false)
|
||||||
require.NotNil(t, err, "Should have failed to import.")
|
require.NotNil(t, err, "Should have failed to import.")
|
||||||
|
|
||||||
_, nErr = th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
|
_, nErr = th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
|
||||||
@ -442,7 +442,7 @@ func TestImportImportRole(t *testing.T) {
|
|||||||
data.Description = ptrStr("description")
|
data.Description = ptrStr("description")
|
||||||
data.Permissions = &[]string{"invite_user", "add_user_to_team"}
|
data.Permissions = &[]string{"invite_user", "add_user_to_team"}
|
||||||
|
|
||||||
err = th.App.importRole(th.Context, &data, false, false)
|
err = th.App.importRole(th.Context, &data, false)
|
||||||
require.Nil(t, err, "Should have succeeded.")
|
require.Nil(t, err, "Should have succeeded.")
|
||||||
|
|
||||||
role, nErr := th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
|
role, nErr := th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
|
||||||
@ -459,8 +459,9 @@ func TestImportImportRole(t *testing.T) {
|
|||||||
data.DisplayName = ptrStr("new display name")
|
data.DisplayName = ptrStr("new display name")
|
||||||
data.Description = ptrStr("description")
|
data.Description = ptrStr("description")
|
||||||
data.Permissions = &[]string{"manage_slash_commands"}
|
data.Permissions = &[]string{"manage_slash_commands"}
|
||||||
|
data.SchemeManaged = model.NewBool(true)
|
||||||
|
|
||||||
err = th.App.importRole(th.Context, &data, false, true)
|
err = th.App.importRole(th.Context, &data, false)
|
||||||
require.Nil(t, err, "Should have succeeded. %v", err)
|
require.Nil(t, err, "Should have succeeded. %v", err)
|
||||||
|
|
||||||
role, nErr = th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
|
role, nErr = th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
|
||||||
@ -479,7 +480,7 @@ func TestImportImportRole(t *testing.T) {
|
|||||||
DisplayName: ptrStr("new display name again"),
|
DisplayName: ptrStr("new display name again"),
|
||||||
}
|
}
|
||||||
|
|
||||||
err = th.App.importRole(th.Context, &data2, false, false)
|
err = th.App.importRole(th.Context, &data2, false)
|
||||||
require.Nil(t, err, "Should have succeeded.")
|
require.Nil(t, err, "Should have succeeded.")
|
||||||
|
|
||||||
role, nErr = th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
|
role, nErr = th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
|
||||||
@ -490,7 +491,7 @@ func TestImportImportRole(t *testing.T) {
|
|||||||
assert.Equal(t, *data.Description, role.Description)
|
assert.Equal(t, *data.Description, role.Description)
|
||||||
assert.Equal(t, *data.Permissions, role.Permissions)
|
assert.Equal(t, *data.Permissions, role.Permissions)
|
||||||
assert.False(t, role.BuiltIn)
|
assert.False(t, role.BuiltIn)
|
||||||
assert.False(t, role.SchemeManaged)
|
assert.True(t, role.SchemeManaged)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestImportImportTeam(t *testing.T) {
|
func TestImportImportTeam(t *testing.T) {
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
type LineImportData struct {
|
type LineImportData struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
|
Role *RoleImportData `json:"role,omitempty"`
|
||||||
Scheme *SchemeImportData `json:"scheme,omitempty"`
|
Scheme *SchemeImportData `json:"scheme,omitempty"`
|
||||||
Team *TeamImportData `json:"team,omitempty"`
|
Team *TeamImportData `json:"team,omitempty"`
|
||||||
Channel *ChannelImportData `json:"channel,omitempty"`
|
Channel *ChannelImportData `json:"channel,omitempty"`
|
||||||
@ -208,10 +209,11 @@ type SchemeImportData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RoleImportData struct {
|
type RoleImportData struct {
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
DisplayName *string `json:"display_name"`
|
DisplayName *string `json:"display_name"`
|
||||||
Description *string `json:"description"`
|
Description *string `json:"description"`
|
||||||
Permissions *[]string `json:"permissions"`
|
Permissions *[]string `json:"permissions"`
|
||||||
|
SchemeManaged *bool `json:"scheme_managed"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LineImportWorkerData struct {
|
type LineImportWorkerData struct {
|
||||||
|
@ -48,6 +48,11 @@ func MakeWorker(jobServer *jobs.JobServer, app AppIface) *jobs.SimpleWorker {
|
|||||||
opts.IncludeProfilePictures = true
|
opts.IncludeProfilePictures = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
includeRolesAndSchemes, ok := job.Data["include_roles_and_schemes"]
|
||||||
|
if ok && includeRolesAndSchemes == "true" {
|
||||||
|
opts.IncludeRolesAndSchemes = true
|
||||||
|
}
|
||||||
|
|
||||||
outPath := *app.Config().ExportSettings.Directory
|
outPath := *app.Config().ExportSettings.Directory
|
||||||
exportFilename := job.Id + "_export.zip"
|
exportFilename := job.Id + "_export.zip"
|
||||||
|
|
||||||
|
@ -102,6 +102,7 @@ func init() {
|
|||||||
ExportCreateCmd.Flags().Bool("no-attachments", false, "Exclude file attachments from the export file.")
|
ExportCreateCmd.Flags().Bool("no-attachments", false, "Exclude file attachments from the export file.")
|
||||||
ExportCreateCmd.Flags().Bool("include-archived-channels", false, "Include archived channels in the export file.")
|
ExportCreateCmd.Flags().Bool("include-archived-channels", false, "Include archived channels in the export file.")
|
||||||
ExportCreateCmd.Flags().Bool("include-profile-pictures", false, "Include profile pictures in the export file.")
|
ExportCreateCmd.Flags().Bool("include-profile-pictures", false, "Include profile pictures in the export file.")
|
||||||
|
ExportCreateCmd.Flags().Bool("no-roles-and-schemes", false, "Exclude roles and custom permission schemes from the export file.")
|
||||||
|
|
||||||
ExportDownloadCmd.Flags().Bool("resume", false, "Set to true to resume an export download.")
|
ExportDownloadCmd.Flags().Bool("resume", false, "Set to true to resume an export download.")
|
||||||
_ = ExportDownloadCmd.Flags().MarkHidden("resume")
|
_ = ExportDownloadCmd.Flags().MarkHidden("resume")
|
||||||
@ -138,6 +139,11 @@ func exportCreateCmdF(c client.Client, command *cobra.Command, args []string) er
|
|||||||
data["include_attachments"] = "true"
|
data["include_attachments"] = "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
excludeRolesAndSchemes, _ := command.Flags().GetBool("no-roles-and-schemes")
|
||||||
|
if !excludeRolesAndSchemes {
|
||||||
|
data["include_roles_and_schemes"] = "true"
|
||||||
|
}
|
||||||
|
|
||||||
includeArchivedChannels, _ := command.Flags().GetBool("include-archived-channels")
|
includeArchivedChannels, _ := command.Flags().GetBool("include-archived-channels")
|
||||||
if includeArchivedChannels {
|
if includeArchivedChannels {
|
||||||
data["include_archived_channels"] = "true"
|
data["include_archived_channels"] = "true"
|
||||||
|
@ -145,6 +145,7 @@ func (s *MmctlE2ETestSuite) TestExportCreateCmdF() {
|
|||||||
s.Require().Len(printer.GetLines(), 1)
|
s.Require().Len(printer.GetLines(), 1)
|
||||||
s.Require().Empty(printer.GetErrorLines())
|
s.Require().Empty(printer.GetErrorLines())
|
||||||
s.Require().Equal("true", printer.GetLines()[0].(*model.Job).Data["include_attachments"])
|
s.Require().Equal("true", printer.GetLines()[0].(*model.Job).Data["include_attachments"])
|
||||||
|
s.Require().Equal("true", printer.GetLines()[0].(*model.Job).Data["include_roles_and_schemes"])
|
||||||
})
|
})
|
||||||
|
|
||||||
s.RunForSystemAdminAndLocal("MM-T3878 - create export without attachments", func(c client.Client) {
|
s.RunForSystemAdminAndLocal("MM-T3878 - create export without attachments", func(c client.Client) {
|
||||||
@ -158,7 +159,21 @@ func (s *MmctlE2ETestSuite) TestExportCreateCmdF() {
|
|||||||
s.Require().Nil(err)
|
s.Require().Nil(err)
|
||||||
s.Require().Len(printer.GetLines(), 1)
|
s.Require().Len(printer.GetLines(), 1)
|
||||||
s.Require().Empty(printer.GetErrorLines())
|
s.Require().Empty(printer.GetErrorLines())
|
||||||
s.Require().Empty(printer.GetLines()[0].(*model.Job).Data)
|
s.Require().Equal("", printer.GetLines()[0].(*model.Job).Data["include_attachments"])
|
||||||
|
})
|
||||||
|
|
||||||
|
s.RunForSystemAdminAndLocal("create export without roles and schemes", func(c client.Client) {
|
||||||
|
printer.Clean()
|
||||||
|
|
||||||
|
cmd := &cobra.Command{}
|
||||||
|
|
||||||
|
cmd.Flags().Bool("no-roles-and-schemes", true, "")
|
||||||
|
|
||||||
|
err := exportCreateCmdF(c, cmd, nil)
|
||||||
|
s.Require().Nil(err)
|
||||||
|
s.Require().Len(printer.GetLines(), 1)
|
||||||
|
s.Require().Empty(printer.GetErrorLines())
|
||||||
|
s.Require().Equal("", printer.GetLines()[0].(*model.Job).Data["include_roles_and_schemes"])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,10 @@ func (s *MmctlUnitTestSuite) TestExportCreateCmdF() {
|
|||||||
printer.Clean()
|
printer.Clean()
|
||||||
mockJob := &model.Job{
|
mockJob := &model.Job{
|
||||||
Type: model.JobTypeExportProcess,
|
Type: model.JobTypeExportProcess,
|
||||||
Data: map[string]string{"include_attachments": "true"},
|
Data: map[string]string{
|
||||||
|
"include_attachments": "true",
|
||||||
|
"include_roles_and_schemes": "true",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s.client.
|
s.client.
|
||||||
@ -39,7 +42,9 @@ func (s *MmctlUnitTestSuite) TestExportCreateCmdF() {
|
|||||||
printer.Clean()
|
printer.Clean()
|
||||||
mockJob := &model.Job{
|
mockJob := &model.Job{
|
||||||
Type: model.JobTypeExportProcess,
|
Type: model.JobTypeExportProcess,
|
||||||
Data: make(map[string]string),
|
Data: map[string]string{
|
||||||
|
"include_roles_and_schemes": "true",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s.client.
|
s.client.
|
||||||
@ -57,6 +62,31 @@ func (s *MmctlUnitTestSuite) TestExportCreateCmdF() {
|
|||||||
s.Empty(printer.GetErrorLines())
|
s.Empty(printer.GetErrorLines())
|
||||||
s.Equal(mockJob, printer.GetLines()[0].(*model.Job))
|
s.Equal(mockJob, printer.GetLines()[0].(*model.Job))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
s.Run("create export without roles and schemes", func() {
|
||||||
|
printer.Clean()
|
||||||
|
mockJob := &model.Job{
|
||||||
|
Type: model.JobTypeExportProcess,
|
||||||
|
Data: map[string]string{
|
||||||
|
"include_attachments": "true",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
s.client.
|
||||||
|
EXPECT().
|
||||||
|
CreateJob(context.TODO(), mockJob).
|
||||||
|
Return(mockJob, &model.Response{}, nil).
|
||||||
|
Times(1)
|
||||||
|
|
||||||
|
cmd := &cobra.Command{}
|
||||||
|
cmd.Flags().Bool("no-roles-and-schemes", true, "")
|
||||||
|
|
||||||
|
err := exportCreateCmdF(s.client, cmd, nil)
|
||||||
|
s.Require().Nil(err)
|
||||||
|
s.Len(printer.GetLines(), 1)
|
||||||
|
s.Empty(printer.GetErrorLines())
|
||||||
|
s.Equal(mockJob, printer.GetLines()[0].(*model.Job))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MmctlUnitTestSuite) TestExportDeleteCmdF() {
|
func (s *MmctlUnitTestSuite) TestExportDeleteCmdF() {
|
||||||
|
@ -366,6 +366,7 @@ func importJobListCmdF(c client.Client, command *cobra.Command, args []string) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Statistics struct {
|
type Statistics struct {
|
||||||
|
Roles uint64 `json:"roles"`
|
||||||
Schemes uint64 `json:"schemes"`
|
Schemes uint64 `json:"schemes"`
|
||||||
Teams uint64 `json:"teams"`
|
Teams uint64 `json:"teams"`
|
||||||
Channels uint64 `json:"channels"`
|
Channels uint64 `json:"channels"`
|
||||||
@ -495,6 +496,7 @@ func importValidateCmdF(command *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stat := Statistics{
|
stat := Statistics{
|
||||||
|
Roles: validator.Roles(),
|
||||||
Schemes: validator.Schemes(),
|
Schemes: validator.Schemes(),
|
||||||
Teams: validator.TeamCount(),
|
Teams: validator.TeamCount(),
|
||||||
Channels: validator.ChannelCount(),
|
Channels: validator.ChannelCount(),
|
||||||
@ -542,6 +544,7 @@ func configurePrinter() {
|
|||||||
|
|
||||||
func printStatistics(stat Statistics) {
|
func printStatistics(stat Statistics) {
|
||||||
tmpl := "\n" +
|
tmpl := "\n" +
|
||||||
|
"Roles {{ .Roles }}\n" +
|
||||||
"Schemes {{ .Schemes }}\n" +
|
"Schemes {{ .Schemes }}\n" +
|
||||||
"Teams {{ .Teams }}\n" +
|
"Teams {{ .Teams }}\n" +
|
||||||
"Channels {{ .Channels }}\n" +
|
"Channels {{ .Channels }}\n" +
|
||||||
|
@ -58,6 +58,7 @@ type Validator struct { //nolint:govet
|
|||||||
attachmentsUsed map[string]uint64
|
attachmentsUsed map[string]uint64
|
||||||
allFileNames []string
|
allFileNames []string
|
||||||
|
|
||||||
|
roles map[string]ImportFileInfo
|
||||||
schemes map[string]ImportFileInfo
|
schemes map[string]ImportFileInfo
|
||||||
teams map[string]ImportFileInfo
|
teams map[string]ImportFileInfo
|
||||||
channels map[ChannelTeam]ImportFileInfo
|
channels map[ChannelTeam]ImportFileInfo
|
||||||
@ -75,6 +76,7 @@ type Validator struct { //nolint:govet
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
LineTypeVersion = "version"
|
LineTypeVersion = "version"
|
||||||
|
LineTypeRole = "role"
|
||||||
LineTypeScheme = "scheme"
|
LineTypeScheme = "scheme"
|
||||||
LineTypeTeam = "team"
|
LineTypeTeam = "team"
|
||||||
LineTypeChannel = "channel"
|
LineTypeChannel = "channel"
|
||||||
@ -110,6 +112,7 @@ func NewValidator(
|
|||||||
attachments: make(map[string]*zip.File),
|
attachments: make(map[string]*zip.File),
|
||||||
attachmentsUsed: make(map[string]uint64),
|
attachmentsUsed: make(map[string]uint64),
|
||||||
|
|
||||||
|
roles: map[string]ImportFileInfo{},
|
||||||
schemes: map[string]ImportFileInfo{},
|
schemes: map[string]ImportFileInfo{},
|
||||||
teams: map[string]ImportFileInfo{},
|
teams: map[string]ImportFileInfo{},
|
||||||
channels: map[ChannelTeam]ImportFileInfo{},
|
channels: map[ChannelTeam]ImportFileInfo{},
|
||||||
@ -121,6 +124,10 @@ func NewValidator(
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Validator) Roles() uint64 {
|
||||||
|
return uint64(len(v.roles))
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Validator) Schemes() uint64 {
|
func (v *Validator) Schemes() uint64 {
|
||||||
return uint64(len(v.schemes))
|
return uint64(len(v.schemes))
|
||||||
}
|
}
|
||||||
@ -388,6 +395,8 @@ func (v *Validator) validateLine(info ImportFileInfo, line imports.LineImportDat
|
|||||||
switch line.Type {
|
switch line.Type {
|
||||||
case LineTypeVersion:
|
case LineTypeVersion:
|
||||||
err = v.validateVersion(info, line)
|
err = v.validateVersion(info, line)
|
||||||
|
case LineTypeRole:
|
||||||
|
err = v.validateRole(info, line)
|
||||||
case LineTypeScheme:
|
case LineTypeScheme:
|
||||||
err = v.validateScheme(info, line)
|
err = v.validateScheme(info, line)
|
||||||
case LineTypeTeam:
|
case LineTypeTeam:
|
||||||
@ -444,6 +453,37 @@ func (v *Validator) validateVersion(info ImportFileInfo, line imports.LineImport
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Validator) validateRole(info ImportFileInfo, line imports.LineImportData) (err error) {
|
||||||
|
ivErr := validateNotNil(info, "role", line.Role, func(data imports.RoleImportData) *ImportValidationError {
|
||||||
|
appErr := imports.ValidateRoleImportData(&data)
|
||||||
|
if appErr != nil {
|
||||||
|
return &ImportValidationError{
|
||||||
|
ImportFileInfo: info,
|
||||||
|
FieldName: "role",
|
||||||
|
Err: appErr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.Name != nil {
|
||||||
|
if existing, ok := v.roles[*data.Name]; ok {
|
||||||
|
return &ImportValidationError{
|
||||||
|
ImportFileInfo: info,
|
||||||
|
FieldName: "role",
|
||||||
|
Err: fmt.Errorf("duplicate entry, previous was in line: %d", existing.CurrentLine),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v.roles[*data.Name] = info
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if ivErr != nil {
|
||||||
|
return v.onError(ivErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Validator) validateScheme(info ImportFileInfo, line imports.LineImportData) (err error) {
|
func (v *Validator) validateScheme(info ImportFileInfo, line imports.LineImportData) (err error) {
|
||||||
ivErr := validateNotNil(info, "scheme", line.Scheme, func(data imports.SchemeImportData) *ImportValidationError {
|
ivErr := validateNotNil(info, "scheme", line.Scheme, func(data imports.SchemeImportData) *ImportValidationError {
|
||||||
appErr := imports.ValidateSchemeImportData(&data)
|
appErr := imports.ValidateSchemeImportData(&data)
|
||||||
|
@ -24,6 +24,7 @@ Options
|
|||||||
--include-archived-channels Include archived channels in the export file.
|
--include-archived-channels Include archived channels in the export file.
|
||||||
--include-profile-pictures Include profile pictures in the export file.
|
--include-profile-pictures Include profile pictures in the export file.
|
||||||
--no-attachments Exclude file attachments from the export file.
|
--no-attachments Exclude file attachments from the export file.
|
||||||
|
--no-roles-and-schemes Exclude roles and custom permission schemes from the export file.
|
||||||
|
|
||||||
Options inherited from parent commands
|
Options inherited from parent commands
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -5474,6 +5474,10 @@
|
|||||||
"id": "app.import.import_line.null_post.error",
|
"id": "app.import.import_line.null_post.error",
|
||||||
"translation": "Import data line has type \"post\" but the post object is null."
|
"translation": "Import data line has type \"post\" but the post object is null."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "app.import.import_line.null_role.error",
|
||||||
|
"translation": "Import data line has type \"role\" but the role object is null."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "app.import.import_line.null_scheme.error",
|
"id": "app.import.import_line.null_scheme.error",
|
||||||
"translation": "Import data line has type \"scheme\" but the scheme object is null."
|
"translation": "Import data line has type \"scheme\" but the scheme object is null."
|
||||||
@ -6662,6 +6666,10 @@
|
|||||||
"id": "app.scheme.get.app_error",
|
"id": "app.scheme.get.app_error",
|
||||||
"translation": "Unable to get the scheme."
|
"translation": "Unable to get the scheme."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "app.scheme.get_all_page.app_error",
|
||||||
|
"translation": "Unable to get page of schemes."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "app.scheme.permanent_delete_all.app_error",
|
"id": "app.scheme.permanent_delete_all.app_error",
|
||||||
"translation": "We could not permanently delete the schemes."
|
"translation": "We could not permanently delete the schemes."
|
||||||
@ -9790,6 +9798,10 @@
|
|||||||
"id": "model.reporting_base_options.is_valid.bad_date_range",
|
"id": "model.reporting_base_options.is_valid.bad_date_range",
|
||||||
"translation": "Date range provided is invalid."
|
"translation": "Date range provided is invalid."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "model.scheme.is_valid.app_error",
|
||||||
|
"translation": "Invalid scheme."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "model.search_params_list.is_valid.include_deleted_channels.app_error",
|
"id": "model.search_params_list.is_valid.include_deleted_channels.app_error",
|
||||||
"translation": "All IncludeDeletedChannels params should have the same value."
|
"translation": "All IncludeDeletedChannels params should have the same value."
|
||||||
|
@ -11,5 +11,6 @@ type BulkExportOpts struct {
|
|||||||
IncludeAttachments bool
|
IncludeAttachments bool
|
||||||
IncludeProfilePictures bool
|
IncludeProfilePictures bool
|
||||||
IncludeArchivedChannels bool
|
IncludeArchivedChannels bool
|
||||||
|
IncludeRolesAndSchemes bool
|
||||||
CreateArchive bool
|
CreateArchive bool
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user