github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/rbac/rbac.go (about)

     1  package rbac
     2  
     3  import (
     4  	"errors"
     5  
     6  	"gorm.io/gorm"
     7  )
     8  
     9  var (
    10  	ErrRoleNotFound       = errors.New("role not found")
    11  	ErrPermNotFound       = errors.New("permission not found")
    12  	ErrDeleteAssignedPerm = errors.New("cannot delete assigned permission")
    13  )
    14  
    15  // UserRole represents the relationship between users and roles
    16  type UserRole struct {
    17  	ID     uint
    18  	UserID uint
    19  	RoleID uint
    20  }
    21  
    22  // TableName sets the table name
    23  func (u UserRole) TableName() string { return tablePrefix + "user_roles" }
    24  
    25  // Role represents the database model of roles
    26  type Role struct {
    27  	ID   uint
    28  	Name string
    29  }
    30  
    31  // TableName sets the table name
    32  func (r Role) TableName() string { return tablePrefix + "roles" }
    33  
    34  // RolePerm stores the relationship between roles and permissions
    35  type RolePerm struct {
    36  	ID     uint
    37  	RoleID uint
    38  	PermID uint
    39  }
    40  
    41  // TableName sets the table name
    42  func (r RolePerm) TableName() string { return tablePrefix + "role_perms" }
    43  
    44  // Perm represents the database model of permissions
    45  type Perm struct {
    46  	ID   uint
    47  	Name string
    48  }
    49  
    50  // TableName sets the table name
    51  func (p Perm) TableName() string { return tablePrefix + "perms" }
    52  
    53  // Rbac helps deal with permissions
    54  type Rbac struct {
    55  	DB *gorm.DB
    56  }
    57  
    58  // Options has the options for initiating the package.
    59  type Options struct {
    60  	DB           *gorm.DB
    61  	TablesPrefix string
    62  }
    63  
    64  var (
    65  	tablePrefix string
    66  	rbac        *Rbac
    67  )
    68  
    69  // New initiates authority.
    70  func New(opts Options) *Rbac {
    71  	tablePrefix = opts.TablesPrefix
    72  	rbac = &Rbac{DB: opts.DB}
    73  	migrateTables(opts.DB)
    74  	return rbac
    75  }
    76  
    77  // Instance returns the initiated instance.
    78  func Instance() *Rbac { return rbac }
    79  
    80  // NewRole stores a role in the database it accepts the role name.
    81  func (a *Rbac) NewRole(roleName string) error {
    82  	var dbRole Role
    83  	r := a.DB.Where("name=?", roleName).First(&dbRole)
    84  	if r.Error != nil && errors.Is(r.Error, gorm.ErrRecordNotFound) {
    85  		return a.DB.Create(&Role{Name: roleName}).Error
    86  	}
    87  
    88  	return r.Error
    89  }
    90  
    91  // NewPerm stores a permission in the database it accepts the permission name.
    92  func (a *Rbac) NewPerm(permName string) error {
    93  	perm := Perm{}
    94  	r := a.DB.Where("name=?", permName).First(&perm)
    95  	if r.Error != nil && errors.Is(r.Error, gorm.ErrRecordNotFound) {
    96  		return a.DB.Create(&Perm{Name: permName}).Error
    97  	}
    98  
    99  	return r.Error
   100  }
   101  
   102  // AssignPerms assigns a group of permissions to a given role
   103  // it accepts in the first parameter the role name, it returns an error if there is not matching record
   104  // of the role name in the database.
   105  // the second parameter is a slice of strings which represents a group of permissions to be assigned to the role
   106  // if any of these permissions doesn't have a matching record in the database the operations stops, changes reverted
   107  // and error is returned
   108  // in case of success nothing is returned
   109  func (a *Rbac) AssignPerms(roleName string, permNames ...string) error {
   110  	var role Role
   111  	if r := a.DB.Where("name=?", roleName).First(&role); r.Error != nil {
   112  		if errors.Is(r.Error, gorm.ErrRecordNotFound) {
   113  			return ErrRoleNotFound
   114  		}
   115  		return r.Error
   116  	}
   117  
   118  	var perms []Perm
   119  	for _, permName := range permNames {
   120  		var perm Perm
   121  		if r := a.DB.Where("name=?", permName).First(&perm); r.Error != nil {
   122  			if errors.Is(r.Error, gorm.ErrRecordNotFound) {
   123  				return ErrPermNotFound
   124  			}
   125  			return r.Error
   126  		}
   127  
   128  		perms = append(perms, perm)
   129  	}
   130  
   131  	// insert data into RolePermissions table
   132  	for _, perm := range perms {
   133  		// ignore any assigned permission
   134  		var rolePerm RolePerm
   135  		if r := a.DB.Where("role_id=?", role.ID).Where("perm_id =?", perm.ID).First(&rolePerm); r.Error != nil { // assign the record
   136  			if cRes := a.DB.Create(&RolePerm{RoleID: role.ID, PermID: perm.ID}); cRes.Error != nil {
   137  				return cRes.Error
   138  			}
   139  		}
   140  	}
   141  
   142  	return nil
   143  }
   144  
   145  // AssignRole assigns a given role to a user
   146  // the first parameter is the user id, the second parameter is the role name
   147  // if the role name doesn't have a matching record in the data base an error is returned
   148  // if the user have already a role assigned to him an error is returned
   149  func (a *Rbac) AssignRole(userID uint, roleName string) error {
   150  	// make sure the role exist
   151  	var role Role
   152  	if r := a.DB.Where("name=?", roleName).First(&role); r.Error != nil {
   153  		if errors.Is(r.Error, gorm.ErrRecordNotFound) {
   154  			return ErrRoleNotFound
   155  		}
   156  		return r.Error
   157  	}
   158  
   159  	// check if the role is already assigned
   160  	userRole := UserRole{}
   161  	if r := a.DB.Where("user_id=?", userID).Where("role_id=?", role.ID).First(&userRole); r.Error == nil {
   162  		return nil
   163  	}
   164  
   165  	// assign the role
   166  	a.DB.Create(&UserRole{UserID: userID, RoleID: role.ID})
   167  
   168  	return nil
   169  }
   170  
   171  // CheckRole checks if a role is assigned to a user it accepts the user id as the first parameter
   172  // the role as the second parameter
   173  // it returns an error if the role is not present in database.
   174  func (a *Rbac) CheckRole(userID uint, roleName string) (bool, error) {
   175  	// find the role
   176  	var role Role
   177  	if r := a.DB.Where("name=?", roleName).First(&role); r.Error != nil {
   178  		if errors.Is(r.Error, gorm.ErrRecordNotFound) {
   179  			return false, ErrRoleNotFound
   180  		}
   181  		return false, r.Error
   182  	}
   183  
   184  	// check if the role is assigned
   185  	userRole := UserRole{}
   186  	if r := a.DB.Where("user_id=?", userID).Where("role_id=?", role.ID).First(&userRole); r.Error != nil {
   187  		if errors.Is(r.Error, gorm.ErrRecordNotFound) {
   188  			return false, nil
   189  		}
   190  		return false, r.Error
   191  	}
   192  
   193  	return true, nil
   194  }
   195  
   196  // CheckPerm checks if a permission is assigned to the role that's assigned to the user.
   197  // it accepts the user id as the first parameter
   198  // the permission as the second parameter
   199  // it returns an error if the permission is not present in the database
   200  func (a *Rbac) CheckPerm(userID uint, permName string) (bool, error) {
   201  	var userRoles []UserRole
   202  	if r := a.DB.Where("user_id=?", userID).Find(&userRoles); r.Error != nil {
   203  		if errors.Is(r.Error, gorm.ErrRecordNotFound) {
   204  			return false, nil
   205  		}
   206  		return false, r.Error
   207  	}
   208  
   209  	// prepare an array of role ids
   210  	var roleIDs []uint
   211  	for _, r := range userRoles {
   212  		roleIDs = append(roleIDs, r.RoleID)
   213  	}
   214  
   215  	// find the permission
   216  	var perm Perm
   217  	if r := a.DB.Where("name=?", permName).First(&perm); r.Error != nil {
   218  		if errors.Is(r.Error, gorm.ErrRecordNotFound) {
   219  			return false, ErrPermNotFound
   220  		}
   221  		return false, r.Error
   222  	}
   223  
   224  	// find the role permission
   225  	var rolePerm RolePerm
   226  	r := a.DB.Where("role_id IN (?)", roleIDs).Where("perm_id=?", perm.ID).First(&rolePerm)
   227  	return r.Error == nil, nil
   228  }
   229  
   230  // CheckRolePerm checks if a role has the permission assigned
   231  // it accepts the role as the first parameter
   232  // it accepts the permission as the second parameter
   233  // it returns an error if the role is not present in database
   234  // it returns an error if the permission is not present in database
   235  func (a *Rbac) CheckRolePerm(roleName string, permName string) (bool, error) {
   236  	// find the role
   237  	var role Role
   238  	if r := a.DB.Where("name=?", roleName).First(&role); r.Error != nil {
   239  		if errors.Is(r.Error, gorm.ErrRecordNotFound) {
   240  			return false, errors.New("role not found")
   241  		}
   242  		return false, r.Error
   243  	}
   244  
   245  	// find the permission
   246  	var perm Perm
   247  	if r := a.DB.Where("name=?", permName).First(&perm); r.Error != nil {
   248  		if errors.Is(r.Error, gorm.ErrRecordNotFound) {
   249  			return false, errors.New("permission not found")
   250  		}
   251  		return false, r.Error
   252  	}
   253  
   254  	// find the rolePerm
   255  	var rolePerm RolePerm
   256  	if r := a.DB.Where("role_id=?", role.ID).Where("perm_id=?", perm.ID).First(&rolePerm); r.Error != nil {
   257  		if errors.Is(r.Error, gorm.ErrRecordNotFound) {
   258  			return false, nil
   259  		}
   260  		return false, r.Error
   261  	}
   262  
   263  	return true, nil
   264  }
   265  
   266  // RevokeRole revokes a user's role
   267  // it returns a error in case of any
   268  func (a *Rbac) RevokeRole(userID uint, roleName string) error {
   269  	// find the role
   270  	var role Role
   271  	if r := a.DB.Where("name=?", roleName).First(&role); r.Error != nil {
   272  		if errors.Is(r.Error, gorm.ErrRecordNotFound) {
   273  			return ErrRoleNotFound
   274  		}
   275  		return r.Error
   276  	}
   277  
   278  	// revoke the role
   279  	return a.DB.Where("user_id=?", userID).Where("role_id=?", role.ID).Delete(UserRole{}).Error
   280  }
   281  
   282  // RevokePerm revokes a permission from the user's assigned role
   283  // it returns an error in case of any
   284  func (a *Rbac) RevokePerm(userID uint, permName string) error {
   285  	// revoke the permission from all roles of the user
   286  	// find the user roles
   287  	var userRoles []UserRole
   288  	if r := a.DB.Where("user_id=?", userID).Find(&userRoles); r.Error != nil {
   289  		if errors.Is(r.Error, gorm.ErrRecordNotFound) {
   290  			return nil
   291  		}
   292  		return r.Error
   293  	}
   294  
   295  	// find the permission
   296  	var perm Perm
   297  	if r := a.DB.Where("name=?", permName).First(&perm); r.Error != nil {
   298  		if errors.Is(r.Error, gorm.ErrRecordNotFound) {
   299  			return ErrPermNotFound
   300  		}
   301  		return r.Error
   302  	}
   303  
   304  	for _, r := range userRoles { // revoke the permission
   305  		a.DB.Where("role_id=?", r.RoleID).Where("perm_id=?", perm.ID).Delete(RolePerm{})
   306  	}
   307  
   308  	return nil
   309  }
   310  
   311  // RevokeRolePerm revokes a permission from a given role
   312  // it returns an error in case of any
   313  func (a *Rbac) RevokeRolePerm(roleName string, permName string) error {
   314  	// find the role
   315  	var role Role
   316  	if r := a.DB.Where("name=?", roleName).First(&role); r.Error != nil {
   317  		if errors.Is(r.Error, gorm.ErrRecordNotFound) {
   318  			return ErrRoleNotFound
   319  		}
   320  		return r.Error
   321  	}
   322  
   323  	// find the permission
   324  	var perm Perm
   325  	if r := a.DB.Where("name=?", permName).First(&perm); r.Error != nil {
   326  		if errors.Is(r.Error, gorm.ErrRecordNotFound) {
   327  			return ErrPermNotFound
   328  		}
   329  		return r.Error
   330  	}
   331  
   332  	// revoke the permission
   333  	return a.DB.Where("role_id=?", role.ID).Where("perm_id=?", perm.ID).Delete(RolePerm{}).Error
   334  }
   335  
   336  // GetRoles returns all stored roles
   337  func (a *Rbac) GetRoles() ([]string, error) {
   338  	var result []string
   339  	var roles []Role
   340  	a.DB.Find(&roles)
   341  
   342  	for _, role := range roles {
   343  		result = append(result, role.Name)
   344  	}
   345  
   346  	return result, nil
   347  }
   348  
   349  // GetUserRoles returns all user assigned roles
   350  func (a *Rbac) GetUserRoles(userID uint) ([]string, error) {
   351  	var result []string
   352  	var userRoles []UserRole
   353  	a.DB.Where("user_id=?", userID).Find(&userRoles)
   354  
   355  	for _, r := range userRoles {
   356  		var role Role
   357  		// for every user role get the role name
   358  		if r := a.DB.Where("id=?", r.RoleID).Find(&role); r.Error == nil {
   359  			result = append(result, role.Name)
   360  		}
   361  	}
   362  
   363  	return result, nil
   364  }
   365  
   366  // GetPerms returns all stored permissions
   367  func (a *Rbac) GetPerms() ([]string, error) {
   368  	var result []string
   369  	var perms []Perm
   370  	a.DB.Find(&perms)
   371  
   372  	for _, perm := range perms {
   373  		result = append(result, perm.Name)
   374  	}
   375  
   376  	return result, nil
   377  }
   378  
   379  // DeleteRole deletes a given role
   380  // if the role is assigned to a user it returns an error
   381  func (a *Rbac) DeleteRole(roleName string) error {
   382  	// find the role
   383  	var role Role
   384  	if r := a.DB.Where("name=?", roleName).First(&role); r.Error != nil {
   385  		if errors.Is(r.Error, gorm.ErrRecordNotFound) {
   386  			return ErrRoleNotFound
   387  		}
   388  		return r.Error
   389  	}
   390  
   391  	// check if the role is assigned to a user
   392  	var userRole UserRole
   393  	if r := a.DB.Where("role_id=?", role.ID).First(&userRole); r.Error == nil {
   394  		return ErrDeleteAssignedPerm
   395  	}
   396  
   397  	// revoke the assignment of permissions before deleting the role
   398  	a.DB.Where("role_id=?", role.ID).Delete(RolePerm{})
   399  	// delete the role
   400  	a.DB.Where("name=?", roleName).Delete(Role{})
   401  
   402  	return nil
   403  }
   404  
   405  // DeletePerm deletes a given permission if the permission is assigned to a role it returns an error.
   406  func (a *Rbac) DeletePerm(permName string) error {
   407  	// find the permission
   408  	var perm Perm
   409  	if r := a.DB.Where("name=?", permName).First(&perm); r.Error != nil {
   410  		if errors.Is(r.Error, gorm.ErrRecordNotFound) {
   411  			return ErrPermNotFound
   412  		}
   413  		return r.Error
   414  	}
   415  
   416  	// check if the permission is assigned to a role
   417  	var rolePerm RolePerm
   418  	if r := a.DB.Where("perm_id=?", perm.ID).First(&rolePerm); r.Error == nil {
   419  		return ErrDeleteAssignedPerm
   420  	}
   421  
   422  	// delete the permission
   423  	a.DB.Where("name=?", permName).Delete(Perm{})
   424  
   425  	return nil
   426  }
   427  
   428  func migrateTables(db *gorm.DB) {
   429  	db.AutoMigrate(&Role{})
   430  	db.AutoMigrate(&Perm{})
   431  	db.AutoMigrate(&RolePerm{})
   432  	db.AutoMigrate(&UserRole{})
   433  }