// Copyright (c) 2025 Zeni Kim // 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 }