github.com/vnforks/kid/v5@v5.22.1-0.20200408055009-b89d99c65676/app/permissions_migrations.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package app
     5  
     6  import (
     7  	"strings"
     8  
     9  	"github.com/vnforks/kid/v5/model"
    10  )
    11  
    12  type permissionTransformation struct {
    13  	On     func(*model.Role, map[string]map[string]bool) bool
    14  	Add    []string
    15  	Remove []string
    16  }
    17  type permissionsMap []permissionTransformation
    18  
    19  const (
    20  	PERMISSION_MANAGE_SYSTEM                   = "manage_system"
    21  	PERMISSION_MANAGE_EMOJIS                   = "manage_emojis"
    22  	PERMISSION_MANAGE_OTHERS_EMOJIS            = "manage_others_emojis"
    23  	PERMISSION_CREATE_EMOJIS                   = "create_emojis"
    24  	PERMISSION_DELETE_EMOJIS                   = "delete_emojis"
    25  	PERMISSION_DELETE_OTHERS_EMOJIS            = "delete_others_emojis"
    26  	PERMISSION_MANAGE_WEBHOOKS                 = "manage_webhooks"
    27  	PERMISSION_MANAGE_OTHERS_WEBHOOKS          = "manage_others_webhooks"
    28  	PERMISSION_MANAGE_INCOMING_WEBHOOKS        = "manage_incoming_webhooks"
    29  	PERMISSION_MANAGE_OTHERS_INCOMING_WEBHOOKS = "manage_others_incoming_webhooks"
    30  	PERMISSION_MANAGE_OUTGOING_WEBHOOKS        = "manage_outgoing_webhooks"
    31  	PERMISSION_MANAGE_OTHERS_OUTGOING_WEBHOOKS = "manage_others_outgoing_webhooks"
    32  	PERMISSION_LIST_BRANCHES                   = "list_branches"
    33  	PERMISSION_PERMANENT_DELETE_USER           = "permanent_delete_user"
    34  	PERMISSION_DELETE_CLASS                    = "delete_class"
    35  	PERMISSION_MANAGE_CLASS                    = "manage_class"
    36  	PERMISSION_VIEW_MEMBERS                    = "view_members"
    37  	PERMISSION_USE_CLASS_MENTIONS              = "use_class_mentions"
    38  	PERMISSION_CREATE_POST                     = "create_post"
    39  	PERMISSION_ADD_REACTION                    = "add_reaction"
    40  	PERMISSION_REMOVE_REACTION                 = "remove_reaction"
    41  	PERMISSION_MANAGE_CLASS_MEMBERS            = "manage_class_members"
    42  )
    43  
    44  func isRole(roleName string) func(*model.Role, map[string]map[string]bool) bool {
    45  	return func(role *model.Role, permissionsMap map[string]map[string]bool) bool {
    46  		return role.Name == roleName
    47  	}
    48  }
    49  
    50  func isNotRole(roleName string) func(*model.Role, map[string]map[string]bool) bool {
    51  	return func(role *model.Role, permissionsMap map[string]map[string]bool) bool {
    52  		return role.Name != roleName
    53  	}
    54  }
    55  
    56  func isNotSchemeRole(roleName string) func(*model.Role, map[string]map[string]bool) bool {
    57  	return func(role *model.Role, permissionsMap map[string]map[string]bool) bool {
    58  		return !strings.Contains(role.DisplayName, roleName)
    59  	}
    60  }
    61  
    62  func permissionExists(permission string) func(*model.Role, map[string]map[string]bool) bool {
    63  	return func(role *model.Role, permissionsMap map[string]map[string]bool) bool {
    64  		val, ok := permissionsMap[role.Name][permission]
    65  		return ok && val
    66  	}
    67  }
    68  
    69  func permissionNotExists(permission string) func(*model.Role, map[string]map[string]bool) bool {
    70  	return func(role *model.Role, permissionsMap map[string]map[string]bool) bool {
    71  		val, ok := permissionsMap[role.Name][permission]
    72  		return !(ok && val)
    73  	}
    74  }
    75  
    76  func onOtherRole(otherRole string, function func(*model.Role, map[string]map[string]bool) bool) func(*model.Role, map[string]map[string]bool) bool {
    77  	return func(role *model.Role, permissionsMap map[string]map[string]bool) bool {
    78  		return function(&model.Role{Name: otherRole}, permissionsMap)
    79  	}
    80  }
    81  
    82  func permissionOr(funcs ...func(*model.Role, map[string]map[string]bool) bool) func(*model.Role, map[string]map[string]bool) bool {
    83  	return func(role *model.Role, permissionsMap map[string]map[string]bool) bool {
    84  		for _, f := range funcs {
    85  			if f(role, permissionsMap) {
    86  				return true
    87  			}
    88  		}
    89  		return false
    90  	}
    91  }
    92  
    93  func permissionAnd(funcs ...func(*model.Role, map[string]map[string]bool) bool) func(*model.Role, map[string]map[string]bool) bool {
    94  	return func(role *model.Role, permissionsMap map[string]map[string]bool) bool {
    95  		for _, f := range funcs {
    96  			if !f(role, permissionsMap) {
    97  				return false
    98  			}
    99  		}
   100  		return true
   101  	}
   102  }
   103  
   104  func applyPermissionsMap(role *model.Role, roleMap map[string]map[string]bool, migrationMap permissionsMap) []string {
   105  	var result []string
   106  
   107  	roleName := role.Name
   108  	for _, transformation := range migrationMap {
   109  		if transformation.On(role, roleMap) {
   110  			for _, permission := range transformation.Add {
   111  				roleMap[roleName][permission] = true
   112  			}
   113  			for _, permission := range transformation.Remove {
   114  				roleMap[roleName][permission] = false
   115  			}
   116  		}
   117  	}
   118  
   119  	for key, active := range roleMap[roleName] {
   120  		if active {
   121  			result = append(result, key)
   122  		}
   123  	}
   124  	return result
   125  }
   126  
   127  func (a *App) doPermissionsMigration(key string, migrationMap permissionsMap) *model.AppError {
   128  	if _, err := a.Srv().Store.System().GetByName(key); err == nil {
   129  		return nil
   130  	}
   131  
   132  	roles, err := a.GetAllRoles()
   133  	if err != nil {
   134  		return err
   135  	}
   136  
   137  	roleMap := make(map[string]map[string]bool)
   138  	for _, role := range roles {
   139  		roleMap[role.Name] = make(map[string]bool)
   140  		for _, permission := range role.Permissions {
   141  			roleMap[role.Name][permission] = true
   142  		}
   143  	}
   144  
   145  	for _, role := range roles {
   146  		role.Permissions = applyPermissionsMap(role, roleMap, migrationMap)
   147  		if _, err := a.Srv().Store.Role().Save(role); err != nil {
   148  			return err
   149  		}
   150  	}
   151  
   152  	if err := a.Srv().Store.System().Save(&model.System{Name: key, Value: "true"}); err != nil {
   153  		return err
   154  	}
   155  	return nil
   156  }
   157  
   158  func (a *App) getEmojisPermissionsSplitMigration() (permissionsMap, error) {
   159  	return permissionsMap{
   160  		permissionTransformation{
   161  			On:     permissionExists(PERMISSION_MANAGE_EMOJIS),
   162  			Add:    []string{PERMISSION_CREATE_EMOJIS, PERMISSION_DELETE_EMOJIS},
   163  			Remove: []string{PERMISSION_MANAGE_EMOJIS},
   164  		},
   165  		permissionTransformation{
   166  			On:     permissionExists(PERMISSION_MANAGE_OTHERS_EMOJIS),
   167  			Add:    []string{PERMISSION_DELETE_OTHERS_EMOJIS},
   168  			Remove: []string{PERMISSION_MANAGE_OTHERS_EMOJIS},
   169  		},
   170  	}, nil
   171  }
   172  
   173  func (a *App) getWebhooksPermissionsSplitMigration() (permissionsMap, error) {
   174  	return permissionsMap{
   175  		permissionTransformation{
   176  			On:     permissionExists(PERMISSION_MANAGE_WEBHOOKS),
   177  			Add:    []string{PERMISSION_MANAGE_INCOMING_WEBHOOKS, PERMISSION_MANAGE_OUTGOING_WEBHOOKS},
   178  			Remove: []string{PERMISSION_MANAGE_WEBHOOKS},
   179  		},
   180  		permissionTransformation{
   181  			On:     permissionExists(PERMISSION_MANAGE_OTHERS_WEBHOOKS),
   182  			Add:    []string{PERMISSION_MANAGE_OTHERS_INCOMING_WEBHOOKS, PERMISSION_MANAGE_OTHERS_OUTGOING_WEBHOOKS},
   183  			Remove: []string{PERMISSION_MANAGE_OTHERS_WEBHOOKS},
   184  		},
   185  	}, nil
   186  }
   187  
   188  func (a *App) removePermanentDeleteUserMigration() (permissionsMap, error) {
   189  	return permissionsMap{
   190  		permissionTransformation{
   191  			On:     permissionExists(PERMISSION_PERMANENT_DELETE_USER),
   192  			Remove: []string{PERMISSION_PERMANENT_DELETE_USER},
   193  		},
   194  	}, nil
   195  }
   196  
   197  func (a *App) applyClassManageDeleteToClassUser() (permissionsMap, error) {
   198  	return permissionsMap{
   199  		permissionTransformation{
   200  			On:  permissionAnd(isRole(model.CLASS_USER_ROLE_ID), onOtherRole(model.BRANCH_USER_ROLE_ID, permissionExists(PERMISSION_DELETE_CLASS))),
   201  			Add: []string{PERMISSION_DELETE_CLASS},
   202  		},
   203  		permissionTransformation{
   204  			On:  permissionAnd(isRole(model.CLASS_USER_ROLE_ID), onOtherRole(model.BRANCH_USER_ROLE_ID, permissionExists(PERMISSION_MANAGE_CLASS))),
   205  			Add: []string{PERMISSION_MANAGE_CLASS},
   206  		},
   207  	}, nil
   208  }
   209  
   210  func (a *App) removeClassManageDeleteFromBranchUser() (permissionsMap, error) {
   211  	return permissionsMap{
   212  		permissionTransformation{
   213  			On:     permissionAnd(isRole(model.BRANCH_USER_ROLE_ID), permissionExists(PERMISSION_MANAGE_CLASS)),
   214  			Remove: []string{PERMISSION_MANAGE_CLASS},
   215  		},
   216  		permissionTransformation{
   217  			On:     permissionAnd(isRole(model.BRANCH_USER_ROLE_ID), permissionExists(PERMISSION_DELETE_CLASS)),
   218  			Remove: []string{model.PERMISSION_DELETE_CLASS.Id},
   219  		},
   220  	}, nil
   221  }
   222  
   223  func (a *App) getViewMembersPermissionMigration() (permissionsMap, error) {
   224  	return permissionsMap{
   225  		permissionTransformation{
   226  			On:  isRole(model.SYSTEM_USER_ROLE_ID),
   227  			Add: []string{PERMISSION_VIEW_MEMBERS},
   228  		},
   229  		permissionTransformation{
   230  			On:  isRole(model.SYSTEM_ADMIN_ROLE_ID),
   231  			Add: []string{PERMISSION_VIEW_MEMBERS},
   232  		},
   233  	}, nil
   234  }
   235  
   236  func (a *App) classModerationPermissionsMigration() (permissionsMap, error) {
   237  	transformations := permissionsMap{}
   238  
   239  	var allBranchSchemes []*model.Scheme
   240  	next := a.SchemesIterator(model.SCHEME_SCOPE_BRANCH, 100)
   241  	var schemeBatch []*model.Scheme
   242  	for schemeBatch = next(); len(schemeBatch) > 0; schemeBatch = next() {
   243  		allBranchSchemes = append(allBranchSchemes, schemeBatch...)
   244  	}
   245  
   246  	moderatedPermissionsMinusCreatePost := []string{
   247  		PERMISSION_ADD_REACTION,
   248  		PERMISSION_REMOVE_REACTION,
   249  		PERMISSION_MANAGE_CLASS_MEMBERS,
   250  		PERMISSION_USE_CLASS_MENTIONS,
   251  	}
   252  
   253  	branchAndClassAdminConditionalTransformations := func(branchAdminID, classAdminID, classUserID string) []permissionTransformation {
   254  		transformations := []permissionTransformation{}
   255  
   256  		for _, perm := range moderatedPermissionsMinusCreatePost {
   257  			// add each moderated permission to the class admin if class user or guest has the permission
   258  			trans := permissionTransformation{
   259  				On: permissionAnd(
   260  					isRole(classAdminID),
   261  					permissionOr(
   262  						onOtherRole(classUserID, permissionExists(perm)),
   263  					),
   264  				),
   265  				Add: []string{perm},
   266  			}
   267  			transformations = append(transformations, trans)
   268  
   269  			// add each moderated permission to the branch admin if class admin, user, or guest has the permission
   270  			trans = permissionTransformation{
   271  				On: permissionAnd(
   272  					isRole(branchAdminID),
   273  					permissionOr(
   274  						onOtherRole(classAdminID, permissionExists(perm)),
   275  						onOtherRole(classUserID, permissionExists(perm)),
   276  					),
   277  				),
   278  				Add: []string{perm},
   279  			}
   280  			transformations = append(transformations, trans)
   281  		}
   282  
   283  		return transformations
   284  	}
   285  
   286  	for _, ts := range allBranchSchemes {
   287  		// ensure all branch scheme class admins have create_post because it's not exposed via the UI
   288  		trans := permissionTransformation{
   289  			On:  isRole(ts.DefaultClassAdminRole),
   290  			Add: []string{PERMISSION_CREATE_POST},
   291  		}
   292  		transformations = append(transformations, trans)
   293  
   294  		// ensure all branch scheme branch admins have create_post because it's not exposed via the UI
   295  		trans = permissionTransformation{
   296  			On:  isRole(ts.DefaultBranchAdminRole),
   297  			Add: []string{PERMISSION_CREATE_POST},
   298  		}
   299  		transformations = append(transformations, trans)
   300  
   301  		// conditionally add all other moderated permissions to branch and class admins
   302  		transformations = append(transformations, branchAndClassAdminConditionalTransformations(
   303  			ts.DefaultBranchAdminRole,
   304  			ts.DefaultClassAdminRole,
   305  			ts.DefaultClassUserRole,
   306  		)...)
   307  	}
   308  
   309  	// ensure branch admins have create_post
   310  	transformations = append(transformations, permissionTransformation{
   311  		On:  isRole(model.BRANCH_ADMIN_ROLE_ID),
   312  		Add: []string{PERMISSION_CREATE_POST},
   313  	})
   314  
   315  	// ensure class admins have create_post
   316  	transformations = append(transformations, permissionTransformation{
   317  		On:  isRole(model.CLASS_ADMIN_ROLE_ID),
   318  		Add: []string{PERMISSION_CREATE_POST},
   319  	})
   320  
   321  	// conditionally add all other moderated permissions to branch and class admins
   322  	transformations = append(transformations, branchAndClassAdminConditionalTransformations(
   323  		model.BRANCH_ADMIN_ROLE_ID,
   324  		model.CLASS_ADMIN_ROLE_ID,
   325  		model.CLASS_USER_ROLE_ID,
   326  	)...)
   327  
   328  	// ensure system admin has all of the moderated permissions
   329  	transformations = append(transformations, permissionTransformation{
   330  		On:  isRole(model.SYSTEM_ADMIN_ROLE_ID),
   331  		Add: append(moderatedPermissionsMinusCreatePost, PERMISSION_CREATE_POST),
   332  	})
   333  
   334  	// add the new use_class_mentions permission to everyone who has create_post
   335  	transformations = append(transformations, permissionTransformation{
   336  		On:  permissionOr(permissionExists(PERMISSION_CREATE_POST)),
   337  		Add: []string{PERMISSION_USE_CLASS_MENTIONS},
   338  	})
   339  
   340  	return transformations, nil
   341  }
   342  
   343  // DoPermissionsMigrations execute all the permissions migrations need by the current version.
   344  func (a *App) DoPermissionsMigrations() error {
   345  	PermissionsMigrations := []struct {
   346  		Key       string
   347  		Migration func() (permissionsMap, error)
   348  	}{
   349  		{Key: model.MIGRATION_KEY_EMOJI_PERMISSIONS_SPLIT, Migration: a.getEmojisPermissionsSplitMigration},
   350  		{Key: model.MIGRATION_KEY_WEBHOOK_PERMISSIONS_SPLIT, Migration: a.getWebhooksPermissionsSplitMigration},
   351  		{Key: model.MIGRATION_KEY_REMOVE_PERMANENT_DELETE_USER, Migration: a.removePermanentDeleteUserMigration},
   352  		{Key: model.MIGRATION_KEY_APPLY_CLASS_MANAGE_DELETE_TO_CLASS_USER, Migration: a.applyClassManageDeleteToClassUser},
   353  		{Key: model.MIGRATION_KEY_REMOVE_CLASS_MANAGE_DELETE_FROM_BRANCH_USER, Migration: a.removeClassManageDeleteFromBranchUser},
   354  		{Key: model.MIGRATION_KEY_VIEW_MEMBERS_NEW_PERMISSION, Migration: a.getViewMembersPermissionMigration},
   355  		{Key: model.MIGRATION_KEY_CLASS_MODERATIONS_PERMISSIONS, Migration: a.classModerationPermissionsMigration},
   356  	}
   357  
   358  	for _, migration := range PermissionsMigrations {
   359  		migMap, err := migration.Migration()
   360  		if err != nil {
   361  			return err
   362  		}
   363  		if err := a.doPermissionsMigration(migration.Key, migMap); err != nil {
   364  			return err
   365  		}
   366  	}
   367  	return nil
   368  }