cup/utils/authority.go

436 lines
12 KiB
Go

// 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 uint, roleSlug string) error {
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 = ?", userID).Where("role_id = ?", role.ID).First(&userRole)
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
c.GetGorm().Create(&models.UserRole{UserID: userID, 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 uint, roleSlug 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
}
// check if the role is a assigned
var userRole models.UserRole
res = c.GetGorm().Where("user_id = ?", userID).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 uint, permSlug string) (bool, error) {
// the user role
var userRoles []models.UserRole
res := c.GetGorm().Where("user_id = ?", userID).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
}
// Revokes a user's role
func (a *Authority) RevokeUserRole(c *core.Context, userID uint, roleSlug 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
}
// revoke the role
rRes := c.GetGorm().Where("user_id = ?", userID).Where("role_id = ?", role.ID).Delete(models.UserRole{})
if rRes.Error != nil {
return rRes.Error
}
return nil
}
// Revokes all user's role
func (a *Authority) RevokeAllUserRole(c *core.Context, userID uint) error {
// revoke the role
rRes := c.GetGorm().Where("user_id = ?", userID).Delete(models.UserRole{})
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 uint) ([]models.Role, error) {
var userRoles []models.UserRole
res := c.GetGorm().Where("user_id = ?", userID).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
}