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

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package model
     5  
     6  import (
     7  	"encoding/json"
     8  	"io"
     9  	"strings"
    10  )
    11  
    12  var BuiltInSchemeManagedRoleIDs []string
    13  
    14  func init() {
    15  	BuiltInSchemeManagedRoleIDs = []string{
    16  		SYSTEM_GUEST_ROLE_ID,
    17  		SYSTEM_USER_ROLE_ID,
    18  		SYSTEM_ADMIN_ROLE_ID,
    19  		SYSTEM_POST_ALL_ROLE_ID,
    20  		SYSTEM_POST_ALL_PUBLIC_ROLE_ID,
    21  		SYSTEM_USER_ACCESS_TOKEN_ROLE_ID,
    22  
    23  		BRANCH_USER_ROLE_ID,
    24  		BRANCH_ADMIN_ROLE_ID,
    25  		BRANCH_POST_ALL_ROLE_ID,
    26  
    27  		CLASS_USER_ROLE_ID,
    28  		CLASS_ADMIN_ROLE_ID,
    29  	}
    30  }
    31  
    32  type RoleType string
    33  type RoleScope string
    34  
    35  const (
    36  	SYSTEM_GUEST_ROLE_ID             = "system_guest"
    37  	SYSTEM_USER_ROLE_ID              = "system_user"
    38  	SYSTEM_ADMIN_ROLE_ID             = "system_admin"
    39  	SYSTEM_POST_ALL_ROLE_ID          = "system_post_all"
    40  	SYSTEM_POST_ALL_PUBLIC_ROLE_ID   = "system_post_all_public"
    41  	SYSTEM_USER_ACCESS_TOKEN_ROLE_ID = "system_user_access_token"
    42  
    43  	BRANCH_USER_ROLE_ID     = "branch_user"
    44  	BRANCH_ADMIN_ROLE_ID    = "branch_admin"
    45  	BRANCH_POST_ALL_ROLE_ID = "branch_post_all"
    46  
    47  	CLASS_USER_ROLE_ID  = "class_user"
    48  	CLASS_ADMIN_ROLE_ID = "class_admin"
    49  
    50  	ROLE_NAME_MAX_LENGTH         = 64
    51  	ROLE_DISPLAY_NAME_MAX_LENGTH = 128
    52  	ROLE_DESCRIPTION_MAX_LENGTH  = 1024
    53  
    54  	RoleScopeSystem RoleScope = "System"
    55  	RoleScopeBranch RoleScope = "Branch"
    56  	RoleScopeClass  RoleScope = "Class"
    57  
    58  	RoleTypeGuest RoleType = "Guest"
    59  	RoleTypeUser  RoleType = "User"
    60  	RoleTypeAdmin RoleType = "Admin"
    61  )
    62  
    63  type Role struct {
    64  	Id            string   `json:"id"`
    65  	Name          string   `json:"name"`
    66  	DisplayName   string   `json:"display_name"`
    67  	Description   string   `json:"description"`
    68  	CreateAt      int64    `json:"create_at"`
    69  	UpdateAt      int64    `json:"update_at"`
    70  	DeleteAt      int64    `json:"delete_at"`
    71  	Permissions   []string `json:"permissions"`
    72  	SchemeManaged bool     `json:"scheme_managed"`
    73  	BuiltIn       bool     `json:"built_in"`
    74  }
    75  
    76  type RolePatch struct {
    77  	Permissions *[]string `json:"permissions"`
    78  }
    79  
    80  type RolePermissions struct {
    81  	RoleID      string
    82  	Permissions []string
    83  }
    84  
    85  func (r *Role) ToJson() string {
    86  	b, _ := json.Marshal(r)
    87  	return string(b)
    88  }
    89  
    90  func RoleFromJson(data io.Reader) *Role {
    91  	var r *Role
    92  	json.NewDecoder(data).Decode(&r)
    93  	return r
    94  }
    95  
    96  func RoleListToJson(r []*Role) string {
    97  	b, _ := json.Marshal(r)
    98  	return string(b)
    99  }
   100  
   101  func RoleListFromJson(data io.Reader) []*Role {
   102  	var roles []*Role
   103  	json.NewDecoder(data).Decode(&roles)
   104  	return roles
   105  }
   106  
   107  func (r *RolePatch) ToJson() string {
   108  	b, _ := json.Marshal(r)
   109  	return string(b)
   110  }
   111  
   112  func RolePatchFromJson(data io.Reader) *RolePatch {
   113  	var rolePatch *RolePatch
   114  	json.NewDecoder(data).Decode(&rolePatch)
   115  	return rolePatch
   116  }
   117  
   118  func (r *Role) Patch(patch *RolePatch) {
   119  	if patch.Permissions != nil {
   120  		r.Permissions = *patch.Permissions
   121  	}
   122  }
   123  
   124  // MergeClassHigherScopedPermissions is meant to be invoked on a class scheme's role and merges the higher-scoped
   125  // class role's permissions.
   126  func (r *Role) MergeClassHigherScopedPermissions(higherScopedPermissions *RolePermissions) {
   127  	mergedPermissions := []string{}
   128  
   129  	higherScopedPermissionsMap := AsStringBoolMap(higherScopedPermissions.Permissions)
   130  	rolePermissionsMap := AsStringBoolMap(r.Permissions)
   131  
   132  	for _, cp := range ALL_PERMISSIONS {
   133  		if cp.Scope != PERMISSION_SCOPE_CLASS {
   134  			continue
   135  		}
   136  
   137  		_, presentOnHigherScope := higherScopedPermissionsMap[cp.Id]
   138  
   139  		// For the class admin role always look to the higher scope to determine if the role has ther permission.
   140  		// The class admin is a special case because they're not part of the UI to be "class moderated", only
   141  		// class members and class guests are.
   142  		if higherScopedPermissions.RoleID == CLASS_ADMIN_ROLE_ID && presentOnHigherScope {
   143  			mergedPermissions = append(mergedPermissions, cp.Id)
   144  			continue
   145  		}
   146  
   147  		_, permissionIsModerated := CLASS_MODERATED_PERMISSIONS_MAP[cp.Id]
   148  		if permissionIsModerated {
   149  			_, presentOnRole := rolePermissionsMap[cp.Id]
   150  			if presentOnRole && presentOnHigherScope {
   151  				mergedPermissions = append(mergedPermissions, cp.Id)
   152  			}
   153  		} else {
   154  			if presentOnHigherScope {
   155  				mergedPermissions = append(mergedPermissions, cp.Id)
   156  			}
   157  		}
   158  	}
   159  
   160  	r.Permissions = mergedPermissions
   161  }
   162  
   163  // Returns an array of permissions that are in either role.Permissions
   164  // or patch.Permissions, but not both.
   165  func PermissionsChangedByPatch(role *Role, patch *RolePatch) []string {
   166  	var result []string
   167  
   168  	if patch.Permissions == nil {
   169  		return result
   170  	}
   171  
   172  	roleMap := make(map[string]bool)
   173  	patchMap := make(map[string]bool)
   174  
   175  	for _, permission := range role.Permissions {
   176  		roleMap[permission] = true
   177  	}
   178  
   179  	for _, permission := range *patch.Permissions {
   180  		patchMap[permission] = true
   181  	}
   182  
   183  	for _, permission := range role.Permissions {
   184  		if !patchMap[permission] {
   185  			result = append(result, permission)
   186  		}
   187  	}
   188  
   189  	for _, permission := range *patch.Permissions {
   190  		if !roleMap[permission] {
   191  			result = append(result, permission)
   192  		}
   193  	}
   194  
   195  	return result
   196  }
   197  
   198  func ClassModeratedPermissionsChangedByPatch(role *Role, patch *RolePatch) []string {
   199  	var result []string
   200  
   201  	if patch.Permissions == nil {
   202  		return result
   203  	}
   204  
   205  	roleMap := make(map[string]bool)
   206  	patchMap := make(map[string]bool)
   207  
   208  	for _, permission := range role.Permissions {
   209  		if classModeratedPermissionName, found := CLASS_MODERATED_PERMISSIONS_MAP[permission]; found {
   210  			roleMap[classModeratedPermissionName] = true
   211  		}
   212  	}
   213  
   214  	for _, permission := range *patch.Permissions {
   215  		if classModeratedPermissionName, found := CLASS_MODERATED_PERMISSIONS_MAP[permission]; found {
   216  			patchMap[classModeratedPermissionName] = true
   217  		}
   218  	}
   219  
   220  	for permissionKey := range roleMap {
   221  		if !patchMap[permissionKey] {
   222  			result = append(result, permissionKey)
   223  		}
   224  	}
   225  
   226  	for permissionKey := range patchMap {
   227  		if !roleMap[permissionKey] {
   228  			result = append(result, permissionKey)
   229  		}
   230  	}
   231  
   232  	return result
   233  }
   234  
   235  // GetClassModeratedPermissions returns a map of class moderated permissions that the role has access to
   236  func (r *Role) GetClassModeratedPermissions() map[string]bool {
   237  	moderatedPermissions := make(map[string]bool)
   238  	for _, permission := range r.Permissions {
   239  		if _, found := CLASS_MODERATED_PERMISSIONS_MAP[permission]; !found {
   240  			continue
   241  		}
   242  
   243  		for moderated, moderatedPermissionValue := range CLASS_MODERATED_PERMISSIONS_MAP {
   244  			// the moderated permission has already been found to be true so skip this iteration
   245  			if moderatedPermissions[moderatedPermissionValue] {
   246  				continue
   247  			}
   248  
   249  			if moderated == permission {
   250  				// Special case where the class moderated permission for `manage_members` is different depending on whether the class is private or public
   251  				if moderated == PERMISSION_MANAGE_CLASS_MEMBERS.Id {
   252  					moderatedPermissions[moderatedPermissionValue] = false
   253  				} else {
   254  					moderatedPermissions[moderatedPermissionValue] = true
   255  				}
   256  			}
   257  		}
   258  	}
   259  
   260  	return moderatedPermissions
   261  }
   262  
   263  // RolePatchFromClassModerationsPatch Creates and returns a RolePatch based on a slice of ClassModerationPatchs, roleName is expected to be either "members" or "guests".
   264  func (r *Role) RolePatchFromClassModerationsPatch(classModerationsPatch []*ClassModerationPatch, roleName string) *RolePatch {
   265  	permissionsToAddToPatch := make(map[string]bool)
   266  
   267  	// Iterate through the list of existing permissions on the role and append permissions that we want to keep.
   268  	for _, permission := range r.Permissions {
   269  		// Permission is not moderated so dont add it to the patch and skip the classModerationsPatch
   270  		if _, isModerated := CLASS_MODERATED_PERMISSIONS_MAP[permission]; !isModerated {
   271  			continue
   272  		}
   273  
   274  		permissionEnabled := true
   275  		// Check if permission has a matching moderated permission name inside the class moderation patch
   276  		for _, classModerationPatch := range classModerationsPatch {
   277  			if *classModerationPatch.Name == CLASS_MODERATED_PERMISSIONS_MAP[permission] {
   278  				// Permission key exists in patch with a value of false so skip over it
   279  				if roleName == "members" {
   280  					if classModerationPatch.Roles.Members != nil && !*classModerationPatch.Roles.Members {
   281  						permissionEnabled = false
   282  					}
   283  				}
   284  			}
   285  		}
   286  
   287  		if permissionEnabled {
   288  			permissionsToAddToPatch[permission] = true
   289  		}
   290  	}
   291  
   292  	// Iterate through the patch and add any permissions that dont already exist on the role
   293  	for _, classModerationPatch := range classModerationsPatch {
   294  		for permission, moderatedPermissionName := range CLASS_MODERATED_PERMISSIONS_MAP {
   295  			if roleName == "members" && classModerationPatch.Roles.Members != nil && *classModerationPatch.Roles.Members && *classModerationPatch.Name == moderatedPermissionName {
   296  				permissionsToAddToPatch[permission] = true
   297  			}
   298  
   299  		}
   300  	}
   301  
   302  	patchPermissions := make([]string, 0, len(permissionsToAddToPatch))
   303  	for permission := range permissionsToAddToPatch {
   304  		patchPermissions = append(patchPermissions, permission)
   305  	}
   306  
   307  	return &RolePatch{Permissions: &patchPermissions}
   308  }
   309  
   310  func (r *Role) IsValid() bool {
   311  	if len(r.Id) != 26 {
   312  		return false
   313  	}
   314  
   315  	return r.IsValidWithoutId()
   316  }
   317  
   318  func (r *Role) IsValidWithoutId() bool {
   319  	if !IsValidRoleName(r.Name) {
   320  		return false
   321  	}
   322  
   323  	if len(r.DisplayName) == 0 || len(r.DisplayName) > ROLE_DISPLAY_NAME_MAX_LENGTH {
   324  		return false
   325  	}
   326  
   327  	if len(r.Description) > ROLE_DESCRIPTION_MAX_LENGTH {
   328  		return false
   329  	}
   330  
   331  	for _, permission := range r.Permissions {
   332  		permissionValidated := false
   333  		for _, p := range ALL_PERMISSIONS {
   334  			if permission == p.Id {
   335  				permissionValidated = true
   336  				break
   337  			}
   338  		}
   339  
   340  		if !permissionValidated {
   341  			return false
   342  		}
   343  	}
   344  
   345  	return true
   346  }
   347  
   348  func IsValidRoleName(roleName string) bool {
   349  	if len(roleName) <= 0 || len(roleName) > ROLE_NAME_MAX_LENGTH {
   350  		return false
   351  	}
   352  
   353  	if strings.TrimLeft(roleName, "abcdefghijklmnopqrstuvwxyz0123456789_") != "" {
   354  		return false
   355  	}
   356  
   357  	return true
   358  }
   359  
   360  func MakeDefaultRoles() map[string]*Role {
   361  	roles := make(map[string]*Role)
   362  
   363  	roles[CLASS_USER_ROLE_ID] = &Role{
   364  		Name:        "class_user",
   365  		DisplayName: "authentication.roles.class_user.name",
   366  		Description: "authentication.roles.class_user.description",
   367  		Permissions: []string{
   368  			PERMISSION_READ_CLASS.Id,
   369  			PERMISSION_ADD_REACTION.Id,
   370  			PERMISSION_REMOVE_REACTION.Id,
   371  			PERMISSION_MANAGE_CLASS_MEMBERS.Id,
   372  			PERMISSION_UPLOAD_FILE.Id,
   373  			PERMISSION_CREATE_POST.Id,
   374  			PERMISSION_USE_CLASS_MENTIONS.Id,
   375  			PERMISSION_USE_SLASH_COMMANDS.Id,
   376  		},
   377  		SchemeManaged: true,
   378  		BuiltIn:       true,
   379  	}
   380  
   381  	roles[CLASS_ADMIN_ROLE_ID] = &Role{
   382  		Name:        "class_admin",
   383  		DisplayName: "authentication.roles.class_admin.name",
   384  		Description: "authentication.roles.class_admin.description",
   385  		Permissions: []string{
   386  			PERMISSION_MANAGE_CLASS_ROLES.Id,
   387  		},
   388  		SchemeManaged: true,
   389  		BuiltIn:       true,
   390  	}
   391  
   392  	roles[BRANCH_USER_ROLE_ID] = &Role{
   393  		Name:        "branch_user",
   394  		DisplayName: "authentication.roles.branch_user.name",
   395  		Description: "authentication.roles.branch_user.description",
   396  		Permissions: []string{
   397  			PERMISSION_LIST_BRANCH_CLASSES.Id,
   398  			PERMISSION_READ_CLASS.Id,
   399  			PERMISSION_VIEW_BRANCH.Id,
   400  		},
   401  		SchemeManaged: true,
   402  		BuiltIn:       true,
   403  	}
   404  
   405  	roles[BRANCH_POST_ALL_ROLE_ID] = &Role{
   406  		Name:        "branch_post_all",
   407  		DisplayName: "authentication.roles.branch_post_all.name",
   408  		Description: "authentication.roles.branch_post_all.description",
   409  		Permissions: []string{
   410  			PERMISSION_CREATE_POST.Id,
   411  			PERMISSION_USE_CLASS_MENTIONS.Id,
   412  		},
   413  		SchemeManaged: false,
   414  		BuiltIn:       true,
   415  	}
   416  
   417  	roles[BRANCH_ADMIN_ROLE_ID] = &Role{
   418  		Name:        "branch_admin",
   419  		DisplayName: "authentication.roles.branch_admin.name",
   420  		Description: "authentication.roles.branch_admin.description",
   421  		Permissions: []string{
   422  			PERMISSION_REMOVE_USER_FROM_BRANCH.Id,
   423  			PERMISSION_MANAGE_BRANCH.Id,
   424  			PERMISSION_MANAGE_BRANCH_ROLES.Id,
   425  			PERMISSION_MANAGE_CLASS_ROLES.Id,
   426  			PERMISSION_MANAGE_OTHERS_INCOMING_WEBHOOKS.Id,
   427  			PERMISSION_MANAGE_OTHERS_OUTGOING_WEBHOOKS.Id,
   428  			PERMISSION_MANAGE_SLASH_COMMANDS.Id,
   429  			PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id,
   430  			PERMISSION_MANAGE_INCOMING_WEBHOOKS.Id,
   431  			PERMISSION_MANAGE_OUTGOING_WEBHOOKS.Id,
   432  		},
   433  		SchemeManaged: true,
   434  		BuiltIn:       true,
   435  	}
   436  
   437  	roles[SYSTEM_USER_ROLE_ID] = &Role{
   438  		Name:        "system_user",
   439  		DisplayName: "authentication.roles.global_user.name",
   440  		Description: "authentication.roles.global_user.description",
   441  		Permissions: []string{
   442  			PERMISSION_LIST_BRANCHES.Id,
   443  			PERMISSION_CREATE_CLASS.Id,
   444  			PERMISSION_VIEW_MEMBERS.Id,
   445  		},
   446  		SchemeManaged: true,
   447  		BuiltIn:       true,
   448  	}
   449  
   450  	roles[SYSTEM_POST_ALL_ROLE_ID] = &Role{
   451  		Name:        "system_post_all",
   452  		DisplayName: "authentication.roles.system_post_all.name",
   453  		Description: "authentication.roles.system_post_all.description",
   454  		Permissions: []string{
   455  			PERMISSION_CREATE_POST.Id,
   456  			PERMISSION_USE_CLASS_MENTIONS.Id,
   457  		},
   458  		SchemeManaged: false,
   459  		BuiltIn:       true,
   460  	}
   461  
   462  	roles[SYSTEM_POST_ALL_ROLE_ID] = &Role{
   463  		Name:        "system_post_all_public",
   464  		DisplayName: "authentication.roles.system_post_all_public.name",
   465  		Description: "authentication.roles.system_post_all_public.description",
   466  		Permissions: []string{
   467  			PERMISSION_CREATE_POST.Id,
   468  			PERMISSION_USE_CLASS_MENTIONS.Id,
   469  		},
   470  		SchemeManaged: false,
   471  		BuiltIn:       true,
   472  	}
   473  
   474  	roles[SYSTEM_USER_ACCESS_TOKEN_ROLE_ID] = &Role{
   475  		Name:        "system_user_access_token",
   476  		DisplayName: "authentication.roles.system_user_access_token.name",
   477  		Description: "authentication.roles.system_user_access_token.description",
   478  		Permissions: []string{
   479  			PERMISSION_CREATE_USER_ACCESS_TOKEN.Id,
   480  			PERMISSION_READ_USER_ACCESS_TOKEN.Id,
   481  			PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id,
   482  		},
   483  		SchemeManaged: false,
   484  		BuiltIn:       true,
   485  	}
   486  
   487  	roles[SYSTEM_ADMIN_ROLE_ID] = &Role{
   488  		Name:        "system_admin",
   489  		DisplayName: "authentication.roles.global_admin.name",
   490  		Description: "authentication.roles.global_admin.description",
   491  		// System admins can do anything class and branch admins can do
   492  		// plus everything members of branches and classes can do to all branches
   493  		// and classes on the system
   494  		Permissions: append(
   495  			append(
   496  				append(
   497  					append(
   498  						[]string{
   499  							PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE.Id,
   500  							PERMISSION_MANAGE_SYSTEM.Id,
   501  							PERMISSION_MANAGE_ROLES.Id,
   502  							PERMISSION_MANAGE_CLASS_MEMBERS.Id,
   503  							PERMISSION_DELETE_CLASS.Id,
   504  							PERMISSION_CREATE_CLASS.Id,
   505  							PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH.Id,
   506  							PERMISSION_MANAGE_OTHERS_INCOMING_WEBHOOKS.Id,
   507  							PERMISSION_MANAGE_OTHERS_OUTGOING_WEBHOOKS.Id,
   508  							PERMISSION_EDIT_OTHER_USERS.Id,
   509  							PERMISSION_EDIT_OTHERS_POSTS.Id,
   510  							PERMISSION_MANAGE_OAUTH.Id,
   511  							PERMISSION_DELETE_OTHERS_POSTS.Id,
   512  							PERMISSION_CREATE_BRANCH.Id,
   513  							PERMISSION_ADD_USER_TO_BRANCH.Id,
   514  							PERMISSION_LIST_USERS_WITHOUT_BRANCH.Id,
   515  							PERMISSION_MANAGE_JOBS.Id,
   516  							PERMISSION_CREATE_POST.Id,
   517  							PERMISSION_CREATE_POST_EPHEMERAL.Id,
   518  							PERMISSION_CREATE_USER_ACCESS_TOKEN.Id,
   519  							PERMISSION_READ_USER_ACCESS_TOKEN.Id,
   520  							PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id,
   521  							PERMISSION_LIST_BRANCHES.Id,
   522  							PERMISSION_VIEW_MEMBERS.Id,
   523  						},
   524  						roles[BRANCH_USER_ROLE_ID].Permissions...,
   525  					),
   526  					roles[CLASS_USER_ROLE_ID].Permissions...,
   527  				),
   528  				roles[BRANCH_ADMIN_ROLE_ID].Permissions...,
   529  			),
   530  			roles[CLASS_ADMIN_ROLE_ID].Permissions...,
   531  		),
   532  		SchemeManaged: true,
   533  		BuiltIn:       true,
   534  	}
   535  
   536  	return roles
   537  }