From 6b73e8ee572aa48d8033b2130a5d965cb86e39e5 Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 17 Jun 2023 23:14:38 +0200 Subject: [PATCH] role permission system --- modules/database/database.go | 120 +++++++++++++++++++++++++++++++-- modules/structs/roles.go | 24 +++++++ modules/structs/socket.go | 17 +++-- modules/structs/user.go | 1 + modules/utils/globals.go | 24 +++++++ socketclients/socketclients.go | 56 +++++++++++++++ socketserver/hub.go | 14 ++-- 7 files changed, 243 insertions(+), 13 deletions(-) create mode 100644 modules/structs/roles.go diff --git a/modules/database/database.go b/modules/database/database.go index a94032d..3525fd2 100644 --- a/modules/database/database.go +++ b/modules/database/database.go @@ -4,7 +4,7 @@ import ( "fmt" "janex/admin-dashboard-backend/modules/config" "janex/admin-dashboard-backend/modules/structs" - "log" + "janex/admin-dashboard-backend/modules/utils" "time" "github.com/google/uuid" @@ -23,7 +23,7 @@ func InitDatabase() { &gorm.Config{}) if err != nil { - log.Fatal(err) + panic(err) } DB = db @@ -33,24 +33,134 @@ func InitDatabase() { db.AutoMigrate(&structs.GroupTasks{}) db.AutoMigrate(&structs.GroupTaskSteps{}) db.AutoMigrate(&structs.Scanner{}) + db.AutoMigrate(&structs.Role{}) + db.AutoMigrate(&structs.RolePermission{}) - //createUser() + createDefaultRole() + //adminRoleId := createDefaultRole() + //createUser(adminRoleId) } -func createUser() { +func createUser(roleId string) { pw := []byte("haha") hashedPassword, err := bcrypt.GenerateFromPassword(pw, bcrypt.DefaultCost) if err != nil { - log.Fatal(err) + panic(err) } DB.Create(&structs.User{ Id: uuid.New().String(), + RoleId: roleId, Username: "Alex", Email: "alex@roese.dev", Password: string(hashedPassword), CreatedAt: time.Now(), }) } + +func createDefaultRole() (roleId string) { + // create admin role if not already existing + role := structs.Role{ + Id: uuid.New().String(), + DisplayName: "Admin", + Description: "Management board", + CreatedAt: time.Now(), + } + + var foundRole structs.Role + + DB.First(&foundRole, "display_name = ?", role.DisplayName) + + if foundRole.Id == "" { + result := DB.Create(&role) + + if result.Error != nil { + panic(result.Error) + } + + foundRole.Id = role.Id + } + + // looking for role permissions + var foundRolePermissions []structs.RolePermission + + DB.Where("role_id = ?", foundRole.Id).Find(&foundRolePermissions) + + systemPermissions := utils.GetSystemPermissions() + + if len(foundRolePermissions) > 0 { + // add new permissions if not already present + var newPermissions []string + + for _, systemPermission := range systemPermissions { + if !hasPermission(foundRolePermissions, systemPermission) { + newPermissions = append(newPermissions, systemPermission) + } + } + + if len(newPermissions) > 0 { + var newRolePermissions []structs.RolePermission + + for _, newPermission := range newPermissions { + newRolePermissions = append(newRolePermissions, structs.RolePermission{ + RoleId: foundRole.Id, + PermissionId: newPermission, + }) + } + + DB.Create(newRolePermissions) + } + + // deleting permissions that are no longer supported + var outdatedPermissions []structs.RolePermission + + for _, foundRolePermission := range foundRolePermissions { + if isPermissionOutdated(systemPermissions, foundRolePermission.PermissionId) { + outdatedPermissions = append(outdatedPermissions, foundRolePermission) + } + } + + if len(outdatedPermissions) > 0 { + for _, outdatedPermission := range outdatedPermissions { + DB.Where("role_id = ?", outdatedPermission.RoleId). + Where("permission_id = ?", outdatedPermission.PermissionId). + Delete(&outdatedPermission) + } + } + } else { // admin role has no permissions - grant all permissions + var newRolePermissions []structs.RolePermission + + for _, systemPermission := range systemPermissions { + newRolePermissions = append(newRolePermissions, structs.RolePermission{ + RoleId: foundRole.Id, + PermissionId: systemPermission, + }) + } + + DB.Create(newRolePermissions) + } + + return foundRole.Id +} + +func hasPermission(rolePermissions []structs.RolePermission, permission string) bool { + for _, rolePermission := range rolePermissions { + if rolePermission.PermissionId == permission { + return true + } + } + + return false +} + +func isPermissionOutdated(systemPermissions []string, permission string) bool { + for _, systemPermission := range systemPermissions { + if systemPermission == permission { + return false + } + } + + return true +} diff --git a/modules/structs/roles.go b/modules/structs/roles.go new file mode 100644 index 0000000..cdf3a39 --- /dev/null +++ b/modules/structs/roles.go @@ -0,0 +1,24 @@ +package structs + +import ( + "time" +) + +type Role struct { + Id string + DisplayName string + Description string + UpdatedAt time.Time + CreatedAt time.Time +} + +// Permissions assigned to the role +type RolePermission struct { + RoleId string + PermissionId string +} + +type RolePermissions struct { + RoleId string + Permissions []string +} diff --git a/modules/structs/socket.go b/modules/structs/socket.go index 8ea4370..c382d44 100644 --- a/modules/structs/socket.go +++ b/modules/structs/socket.go @@ -90,10 +90,17 @@ type InitUserSocketConnection struct { GroupTasksSteps []GroupTaskSteps AllUsers []AllUsers Scanners []Scanner + AllRoles []Role + AdminArea InitUserSocketConnectionAdminArea +} + +type InitUserSocketConnectionAdminArea struct { + RolesPermissions []RolePermissions `json:"RolesPermissions,omitempty"` } type AllUsers struct { Id string + RoleId string Avatar string Username string ConnectionStatus uint8 @@ -101,10 +108,12 @@ type AllUsers struct { } type UserData struct { - Id string - Username string - Email string - Sessions []UserSessionSocket + Id string + RoleId string + Username string + Email string + Sessions []UserSessionSocket + Permissions []string } type UserSessionSocket struct { diff --git a/modules/structs/user.go b/modules/structs/user.go index 899115e..2ad64a7 100644 --- a/modules/structs/user.go +++ b/modules/structs/user.go @@ -6,6 +6,7 @@ import ( type User struct { Id string + RoleId string Avatar string Username string Email string diff --git a/modules/utils/globals.go b/modules/utils/globals.go index 92e96af..c41f335 100644 --- a/modules/utils/globals.go +++ b/modules/utils/globals.go @@ -72,3 +72,27 @@ var ( "ScannerName": "required,min=" + minScannerName + ",max=" + maxScannerName, } ) + +const ( + _groupTasks = "group_tasks." + PermissionGroupTasksHistory = _groupTasks + "history" + + _adminArea = "admin_area." + _adminAreaRoles = _adminArea + "roles." + PermissionAdminAreaAddRole = _adminAreaRoles + "add_role" + PermissionAdminAreaUpdateRole = _adminAreaRoles + "update_role" + PermissionAdminAreaDeleteRole = _adminAreaRoles + "delete_role" + PermissionAdminAreaAddUserToRole = _adminAreaRoles + "add_user_to_role" + PermissionAdminAreaLogs = _adminArea + "logs" +) + +func GetSystemPermissions() []string { + return []string{ + PermissionGroupTasksHistory, + PermissionAdminAreaAddRole, + PermissionAdminAreaUpdateRole, + PermissionAdminAreaDeleteRole, + PermissionAdminAreaAddUserToRole, + PermissionAdminAreaLogs, + } +} diff --git a/socketclients/socketclients.go b/socketclients/socketclients.go index 034ae2b..33a9539 100644 --- a/socketclients/socketclients.go +++ b/socketclients/socketclients.go @@ -149,6 +149,7 @@ func GetAllUsers() []structs.AllUsers { for _, user := range users { allUsers = append(allUsers, structs.AllUsers{ Id: user.Id, + RoleId: user.RoleId, Avatar: user.Avatar, Username: user.Username, ConnectionStatus: isUserGenerallyConnected(user.Id), @@ -299,3 +300,58 @@ func isUsernameLengthValid(username string) bool { l := len(username) return l > utils.MinUsername && l < utils.MaxUsername } + +func GetAllRoles() []structs.Role { + var roles []structs.Role + + database.DB.Find(&roles) + + return roles +} + +func GetPermissionsByRoleId(roleId string) []string { + var rolePermissions []structs.RolePermission + + database.DB.Where("role_id = ?", roleId).Find(&rolePermissions) + + var permissions []string + + for _, rolePermission := range rolePermissions { + permissions = append(permissions, rolePermission.PermissionId) + } + + return permissions +} + +// Retrieve all roles with a list of all permissions for each role +func GetAdminAreaRolesPermissions() []structs.RolePermissions { + roles := GetAllRoles() + var rolesPermissions []structs.RolePermission + + database.DB.Find(&rolesPermissions) + + log.Debug().Msgf("rolePermissions: %v", rolesPermissions) + + var rolePermissions []structs.RolePermissions + + for _, role := range roles { + var permissions []string + + for _, rolePermission := range rolesPermissions { + if rolePermission.RoleId == role.Id { + permissions = append(permissions, rolePermission.PermissionId) + } + } + + log.Debug().Msgf("permissions %v", permissions) + + rolePermissions = append(rolePermissions, structs.RolePermissions{ + RoleId: role.Id, + Permissions: permissions, + }) + } + + log.Debug().Msgf("role permissions: %v", rolePermissions) + + return rolePermissions +} diff --git a/socketserver/hub.go b/socketserver/hub.go index 01e0242..c949ed9 100644 --- a/socketserver/hub.go +++ b/socketserver/hub.go @@ -64,16 +64,22 @@ func RunHub() { Cmd: utils.SentCmdInitUserSocketConnection, Body: structs.InitUserSocketConnection{ User: structs.UserData{ - Id: user.Id, - Username: user.Username, - Email: user.Email, - Sessions: socketclients.GetUserSessions(userId), + Id: user.Id, + RoleId: user.RoleId, + Username: user.Username, + Email: user.Email, + Sessions: socketclients.GetUserSessions(userId), + Permissions: socketclients.GetPermissionsByRoleId(user.RoleId), }, CategoryGroups: cache.GetCategoryGroupsSorted(), GroupTasks: grouptasks.GetAllGroupTasks(), GroupTasksSteps: grouptasks.GetAllGroupTasksSteps(), AllUsers: socketclients.GetAllUsers(), Scanners: socketclients.GetAllScanners(), + AllRoles: socketclients.GetAllRoles(), + AdminArea: structs.InitUserSocketConnectionAdminArea{ + RolesPermissions: socketclients.GetAdminAreaRolesPermissions(), + }, }, })