authority system start
This commit is contained in:
parent
e5dee10221
commit
cac2986b59
6 changed files with 476 additions and 0 deletions
16
models/permission.go
Normal file
16
models/permission.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
|
||||||
|
// Use of this source code is governed by MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
type Permission struct {
|
||||||
|
BaseModel
|
||||||
|
Name string
|
||||||
|
Slug string
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName sets the table name
|
||||||
|
func (Permission) TableName() string {
|
||||||
|
return "permissions"
|
||||||
|
}
|
16
models/role-permissions.go
Normal file
16
models/role-permissions.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
|
||||||
|
// Use of this source code is governed by MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
type RolePermission struct {
|
||||||
|
BaseModel
|
||||||
|
RoleID uint // Role id
|
||||||
|
PermissionID uint // Permission id
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName sets the table name
|
||||||
|
func (RolePermission) TableName() string {
|
||||||
|
return "role_permissions"
|
||||||
|
}
|
16
models/roles.go
Normal file
16
models/roles.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
|
||||||
|
// Use of this source code is governed by MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
type Role struct {
|
||||||
|
BaseModel
|
||||||
|
Name string // The name of the role
|
||||||
|
Slug string // String based unique identifier of the role, (use hyphen seperated role name '-', instead of space)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName sets the table name
|
||||||
|
func (Role) TableName() string {
|
||||||
|
return "roles"
|
||||||
|
}
|
16
models/user-roles.go
Normal file
16
models/user-roles.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
|
||||||
|
// Use of this source code is governed by MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
type UserRole struct {
|
||||||
|
BaseModel
|
||||||
|
UserID string // The user id
|
||||||
|
RoleID uint // The role id
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName sets the table name
|
||||||
|
func (UserRole) TableName() string {
|
||||||
|
return "user_roles"
|
||||||
|
}
|
|
@ -18,4 +18,8 @@ func RunAutoMigrations() {
|
||||||
|
|
||||||
// Add auto migrations for your models here...
|
// Add auto migrations for your models here...
|
||||||
db.AutoMigrate(&models.User{})
|
db.AutoMigrate(&models.User{})
|
||||||
|
db.AutoMigrate(&models.UserRole{})
|
||||||
|
db.AutoMigrate(&models.Role{})
|
||||||
|
db.AutoMigrate(&models.RolePermission{})
|
||||||
|
db.AutoMigrate(&models.Permission{})
|
||||||
}
|
}
|
||||||
|
|
408
utils/authority.go
Normal file
408
utils/authority.go
Normal file
|
@ -0,0 +1,408 @@
|
||||||
|
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
|
||||||
|
// Use of this source code is governed by MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.smarteching.com/goffee/core"
|
||||||
|
"git.smarteching.com/goffee/cup/models"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Authority struct{}
|
||||||
|
|
||||||
|
var auth *Authority
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrPermissionInUse = errors.New("cannot delete assigned permission")
|
||||||
|
ErrPermissionNotFound = errors.New("permission not found")
|
||||||
|
ErrRoleInUse = errors.New("cannot delete assigned role")
|
||||||
|
ErrRoleNotFound = errors.New("role not found")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add a new role to the database
|
||||||
|
func (a *Authority) CreateRole(c *core.Context, r models.Role) error {
|
||||||
|
roleSlug := r.Slug
|
||||||
|
var dbRole models.Role
|
||||||
|
res := c.GetGorm().Where("slug = ?", roleSlug).First(&dbRole)
|
||||||
|
if res.Error != nil {
|
||||||
|
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
// create
|
||||||
|
createRes := c.GetGorm().Create(&r)
|
||||||
|
if createRes.Error != nil {
|
||||||
|
return createRes.Error
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New(fmt.Sprintf("role '%v' already exists", roleSlug))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new permission to the database
|
||||||
|
func (a *Authority) CreatePermission(c *core.Context, p models.Permission) error {
|
||||||
|
permSlug := p.Slug
|
||||||
|
var dbPerm models.Permission
|
||||||
|
res := c.GetGorm().Where("slug = ?", permSlug).First(&dbPerm)
|
||||||
|
if res.Error != nil {
|
||||||
|
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
// create
|
||||||
|
createRes := c.GetGorm().Create(&p)
|
||||||
|
if createRes.Error != nil {
|
||||||
|
return createRes.Error
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New(fmt.Sprintf("permission '%v' already exists", permSlug))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assigns a group of permissions to a given role
|
||||||
|
func (a *Authority) AssignPermissionsToRole(c *core.Context, roleSlug string, permSlugs []string) error {
|
||||||
|
var role models.Role
|
||||||
|
rRes := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
|
||||||
|
if rRes.Error != nil {
|
||||||
|
if errors.Is(rRes.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return ErrRoleNotFound
|
||||||
|
}
|
||||||
|
return rRes.Error
|
||||||
|
}
|
||||||
|
var perms []models.Permission
|
||||||
|
for _, permSlug := range permSlugs {
|
||||||
|
var perm models.Permission
|
||||||
|
pRes := c.GetGorm().Where("slug = ?", permSlug).First(&perm)
|
||||||
|
if pRes.Error != nil {
|
||||||
|
if errors.Is(pRes.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return ErrPermissionNotFound
|
||||||
|
}
|
||||||
|
return pRes.Error
|
||||||
|
}
|
||||||
|
perms = append(perms, perm)
|
||||||
|
}
|
||||||
|
tx := c.GetGorm().Begin()
|
||||||
|
for _, perm := range perms {
|
||||||
|
var rolePerm models.RolePermission
|
||||||
|
res := c.GetGorm().Where("role_id = ?", role.ID).Where("permission_id =?", perm.ID).First(&rolePerm)
|
||||||
|
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
cRes := tx.Create(&models.RolePermission{RoleID: role.ID, PermissionID: perm.ID})
|
||||||
|
if cRes.Error != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return cRes.Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
tx.Rollback()
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
if rolePerm != (models.RolePermission{}) {
|
||||||
|
tx.Rollback()
|
||||||
|
return errors.New(fmt.Sprintf("permission '%v' is aleady assigned to the role '%v'", perm.Name, role.Name))
|
||||||
|
}
|
||||||
|
rolePerm = models.RolePermission{}
|
||||||
|
}
|
||||||
|
return tx.Commit().Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assigns a role to a given user
|
||||||
|
func (a *Authority) AssignRoleToUser(c *core.Context, userID interface{}, roleSlug string) error {
|
||||||
|
userIDStr := fmt.Sprintf("%v", userID)
|
||||||
|
var role models.Role
|
||||||
|
res := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
|
||||||
|
if res.Error != nil {
|
||||||
|
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return ErrRoleNotFound
|
||||||
|
}
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
var userRole models.UserRole
|
||||||
|
res = c.GetGorm().Where("user_id = ?", userIDStr).Where("role_id = ?", role.ID).First(&userRole)
|
||||||
|
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
c.GetGorm().Create(&models.UserRole{UserID: userIDStr, RoleID: role.ID})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New(fmt.Sprintf("this role '%v' is aleady assigned to the user", roleSlug))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if a role is assigned to a user
|
||||||
|
func (a *Authority) CheckUserRole(c *core.Context, userID interface{}, roleSlug string) (bool, error) {
|
||||||
|
userIDStr := fmt.Sprintf("%v", userID)
|
||||||
|
// find the role
|
||||||
|
var role models.Role
|
||||||
|
res := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
|
||||||
|
if res.Error != nil {
|
||||||
|
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return false, ErrRoleNotFound
|
||||||
|
}
|
||||||
|
return false, res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the role is a assigned
|
||||||
|
var userRole models.UserRole
|
||||||
|
res = c.GetGorm().Where("user_id = ?", userIDStr).Where("role_id = ?", role.ID).First(&userRole)
|
||||||
|
if res.Error != nil {
|
||||||
|
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if a permission is assigned to a user
|
||||||
|
func (a *Authority) CheckUserPermission(c *core.Context, userID interface{}, permSlug string) (bool, error) {
|
||||||
|
userIDStr := fmt.Sprintf("%v", userID)
|
||||||
|
// the user role
|
||||||
|
var userRoles []models.UserRole
|
||||||
|
res := c.GetGorm().Where("user_id = ?", userIDStr).Find(&userRoles)
|
||||||
|
if res.Error != nil {
|
||||||
|
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
//prepare an array of role ids
|
||||||
|
var roleIDs []interface{}
|
||||||
|
for _, r := range userRoles {
|
||||||
|
roleIDs = append(roleIDs, r.RoleID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the permission
|
||||||
|
var perm models.Permission
|
||||||
|
res = c.GetGorm().Where("slug = ?", permSlug).First(&perm)
|
||||||
|
if res.Error != nil {
|
||||||
|
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return false, ErrPermissionNotFound
|
||||||
|
}
|
||||||
|
return false, res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the role permission
|
||||||
|
var rolePermission models.RolePermission
|
||||||
|
res = c.GetGorm().Where("role_id IN (?)", roleIDs).Where("permission_id = ?", perm.ID).First(&rolePermission)
|
||||||
|
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return false, res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if a permission is assigned to a role
|
||||||
|
func (a *Authority) CheckRolePermission(c *core.Context, roleSlug string, permSlug string) (bool, error) {
|
||||||
|
// find the role
|
||||||
|
var role models.Role
|
||||||
|
res := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
|
||||||
|
if res.Error != nil {
|
||||||
|
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return false, ErrRoleNotFound
|
||||||
|
}
|
||||||
|
return false, res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the permission
|
||||||
|
var perm models.Permission
|
||||||
|
res = c.GetGorm().Where("slug = ?", permSlug).First(&perm)
|
||||||
|
if res.Error != nil {
|
||||||
|
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return false, ErrPermissionNotFound
|
||||||
|
}
|
||||||
|
return false, res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the rolePermission
|
||||||
|
var rolePermission models.RolePermission
|
||||||
|
res = c.GetGorm().Where("role_id = ?", role.ID).Where("permission_id = ?", perm.ID).First(&rolePermission)
|
||||||
|
if res.Error != nil {
|
||||||
|
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revokes a roles's permission
|
||||||
|
func (a *Authority) RevokeRolePermission(c *core.Context, roleSlug string, permSlug string) error {
|
||||||
|
// find the role
|
||||||
|
var role models.Role
|
||||||
|
res := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
|
||||||
|
if res.Error != nil {
|
||||||
|
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return ErrRoleNotFound
|
||||||
|
}
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the permission
|
||||||
|
var perm models.Permission
|
||||||
|
res = c.GetGorm().Where("slug = ?", permSlug).First(&perm)
|
||||||
|
if res.Error != nil {
|
||||||
|
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return ErrPermissionNotFound
|
||||||
|
}
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// revoke the permission
|
||||||
|
rRes := c.GetGorm().Where("role_id = ?", role.ID).Where("permission_id = ?", perm.ID).Delete(models.RolePermission{})
|
||||||
|
if rRes.Error != nil {
|
||||||
|
return rRes.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns all stored roles
|
||||||
|
func (a *Authority) GetAllRoles(c *core.Context) ([]models.Role, error) {
|
||||||
|
var roles []models.Role
|
||||||
|
res := c.GetGorm().Find(&roles)
|
||||||
|
if res.Error != nil {
|
||||||
|
return nil, res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return roles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns all user assigned roles
|
||||||
|
func (a *Authority) GetUserRoles(c *core.Context, userID interface{}) ([]models.Role, error) {
|
||||||
|
userIDStr := fmt.Sprintf("%v", userID)
|
||||||
|
var userRoles []models.UserRole
|
||||||
|
res := c.GetGorm().Where("user_id = ?", userIDStr).Find(&userRoles)
|
||||||
|
if res.Error != nil {
|
||||||
|
return nil, res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
var roleIDs []interface{}
|
||||||
|
for _, r := range userRoles {
|
||||||
|
roleIDs = append(roleIDs, r.RoleID)
|
||||||
|
}
|
||||||
|
|
||||||
|
var roles []models.Role
|
||||||
|
res = c.GetGorm().Where("id IN (?)", roleIDs).Find(&roles)
|
||||||
|
if res.Error != nil {
|
||||||
|
return nil, res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return roles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns all role assigned permissions
|
||||||
|
func (a *Authority) GetRolePermissions(c *core.Context, roleSlug string) ([]models.Permission, error) {
|
||||||
|
var role models.Role
|
||||||
|
res := c.GetGorm().Where("slug = ?", roleSlug).Find(&role)
|
||||||
|
if res.Error != nil {
|
||||||
|
return nil, res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
var rolePerms []models.RolePermission
|
||||||
|
res = c.GetGorm().Where("role_id = ?", role.ID).Find(&rolePerms)
|
||||||
|
if res.Error != nil {
|
||||||
|
return nil, res.Error
|
||||||
|
}
|
||||||
|
var permIDs []interface{}
|
||||||
|
for _, rolePerm := range rolePerms {
|
||||||
|
permIDs = append(permIDs, rolePerm.PermissionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
var perms []models.Permission
|
||||||
|
res = c.GetGorm().Where("id IN (?)", permIDs).Find(&perms)
|
||||||
|
if res.Error != nil {
|
||||||
|
return nil, res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return perms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns all stored permissions
|
||||||
|
func (a *Authority) GetAllPermissions(c *core.Context) ([]models.Permission, error) {
|
||||||
|
var perms []models.Permission
|
||||||
|
res := c.GetGorm().Find(&perms)
|
||||||
|
if res.Error != nil {
|
||||||
|
return nil, res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return perms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes a given role even if it's has assigned permissions
|
||||||
|
func (a *Authority) DeleteRole(c *core.Context, roleSlug string) error {
|
||||||
|
// find the role
|
||||||
|
var role models.Role
|
||||||
|
res := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
|
||||||
|
if res.Error != nil {
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the role is assigned to a user
|
||||||
|
var ca int64
|
||||||
|
res = c.GetGorm().Model(models.UserRole{}).Where("role_id = ?", role.ID).Count(&ca)
|
||||||
|
if res.Error != nil {
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if ca != 0 {
|
||||||
|
// role is assigned
|
||||||
|
return ErrRoleInUse
|
||||||
|
}
|
||||||
|
tx := c.GetGorm().Begin()
|
||||||
|
// revoke the assignment of permissions before deleting the role
|
||||||
|
dRes := tx.Where("role_id = ?", role.ID).Delete(models.RolePermission{})
|
||||||
|
if dRes.Error != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return dRes.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete the role
|
||||||
|
dRes = c.GetGorm().Where("slug = ?", roleSlug).Delete(models.Role{})
|
||||||
|
if dRes.Error != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return dRes.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.Commit().Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes a given permission
|
||||||
|
func (a *Authority) DeletePermission(c *core.Context, permSlug string) error {
|
||||||
|
// find the permission
|
||||||
|
var perm models.Permission
|
||||||
|
res := c.GetGorm().Where("slug = ?", permSlug).First(&perm)
|
||||||
|
if res.Error != nil {
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the permission is assigned to a role
|
||||||
|
var rolePermission models.RolePermission
|
||||||
|
res = c.GetGorm().Where("permission_id = ?", perm.ID).First(&rolePermission)
|
||||||
|
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
|
return res.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Error == nil {
|
||||||
|
return ErrPermissionInUse
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete the permission
|
||||||
|
dRes := c.GetGorm().Where("slug = ?", permSlug).Delete(models.Permission{})
|
||||||
|
if dRes.Error != nil {
|
||||||
|
return dRes.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in a new issue