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 }