rbac system first stage
This commit is contained in:
@@ -2,10 +2,13 @@ package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.haelnorr.com/h/golib/hwsauth"
|
||||
"git.haelnorr.com/h/oslstats/internal/permissions"
|
||||
"git.haelnorr.com/h/oslstats/internal/roles"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/uptrace/bun"
|
||||
@@ -22,8 +25,14 @@ type User struct {
|
||||
DiscordID string `bun:"discord_id,unique"`
|
||||
}
|
||||
|
||||
func (user *User) GetID() int {
|
||||
return user.ID
|
||||
type Users struct {
|
||||
Users []*User
|
||||
Total int
|
||||
PageOpts PageOpts
|
||||
}
|
||||
|
||||
func (u *User) GetID() int {
|
||||
return u.ID
|
||||
}
|
||||
|
||||
// CreateUser creates a new user with the given username and password
|
||||
@@ -114,3 +123,110 @@ func IsUsernameUnique(ctx context.Context, tx bun.Tx, username string) (bool, er
|
||||
}
|
||||
return count == 0, nil
|
||||
}
|
||||
|
||||
// GetRoles loads and returns all roles for this user
|
||||
func (u *User) GetRoles(ctx context.Context, tx bun.Tx) ([]*Role, error) {
|
||||
if u == nil {
|
||||
return nil, errors.New("user cannot be nil")
|
||||
}
|
||||
return GetUserRoles(ctx, tx, u.ID)
|
||||
}
|
||||
|
||||
// GetPermissions loads and returns all permissions for this user (via roles)
|
||||
func (u *User) GetPermissions(ctx context.Context, tx bun.Tx) ([]*Permission, error) {
|
||||
if u == nil {
|
||||
return nil, errors.New("user cannot be nil")
|
||||
}
|
||||
|
||||
// TODO: use proper m2m tables and relations instead of join
|
||||
var permissions []*Permission
|
||||
err := tx.NewSelect().
|
||||
Model(&permissions).
|
||||
Join("JOIN role_permissions AS rp ON rp.permission_id = p.id").
|
||||
Join("JOIN user_roles AS ur ON ur.role_id = rp.role_id").
|
||||
Where("ur.user_id = ?", u.ID).
|
||||
Where("ur.expires_at IS NULL OR ur.expires_at > ?", time.Now().Unix()).
|
||||
Scan(ctx)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return nil, errors.Wrap(err, "tx.NewSelect")
|
||||
}
|
||||
return permissions, nil
|
||||
}
|
||||
|
||||
// HasPermission checks if user has a specific permission (including wildcard check)
|
||||
func (u *User) HasPermission(ctx context.Context, tx bun.Tx, permissionName permissions.Permission) (bool, error) {
|
||||
if u == nil {
|
||||
return false, errors.New("user cannot be nil")
|
||||
}
|
||||
if permissionName == "" {
|
||||
return false, errors.New("permissionName cannot be empty")
|
||||
}
|
||||
|
||||
perms, err := u.GetPermissions(ctx, tx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, p := range perms {
|
||||
if p.Name == permissionName || p.Name == permissions.Wildcard {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// HasRole checks if user has a specific role
|
||||
func (u *User) HasRole(ctx context.Context, tx bun.Tx, roleName roles.Role) (bool, error) {
|
||||
if u == nil {
|
||||
return false, errors.New("user cannot be nil")
|
||||
}
|
||||
return HasRole(ctx, tx, u.ID, roleName)
|
||||
}
|
||||
|
||||
// IsAdmin is a convenience method to check if user has admin role
|
||||
func (u *User) IsAdmin(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
if u == nil {
|
||||
return false, errors.New("user cannot be nil")
|
||||
}
|
||||
return u.HasRole(ctx, tx, "admin")
|
||||
}
|
||||
|
||||
func GetUsers(ctx context.Context, tx bun.Tx, pageOpts *PageOpts) (*Users, error) {
|
||||
if pageOpts == nil {
|
||||
pageOpts = &PageOpts{}
|
||||
}
|
||||
if pageOpts.Page == 0 {
|
||||
pageOpts.Page = 1
|
||||
}
|
||||
if pageOpts.PerPage == 0 {
|
||||
pageOpts.PerPage = 50
|
||||
}
|
||||
if pageOpts.Order == "" {
|
||||
pageOpts.Order = bun.OrderAsc
|
||||
}
|
||||
if pageOpts.OrderBy == "" {
|
||||
pageOpts.OrderBy = "id"
|
||||
}
|
||||
users := []*User{}
|
||||
err := tx.NewSelect().
|
||||
Model(users).
|
||||
OrderBy(pageOpts.OrderBy, pageOpts.Order).
|
||||
Limit(pageOpts.PerPage).
|
||||
Offset(pageOpts.PerPage * (pageOpts.Page - 1)).
|
||||
Scan(ctx)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return nil, errors.Wrap(err, "tx.NewSelect")
|
||||
}
|
||||
total, err := tx.NewSelect().
|
||||
Model(users).
|
||||
Count(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "tx.NewSelect")
|
||||
}
|
||||
list := &Users{
|
||||
Users: users,
|
||||
Total: total,
|
||||
PageOpts: *pageOpts,
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user