Files
comicverse/model/permission.go

146 lines
4.1 KiB
Go

package model
import (
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"strconv"
"strings"
)
type Permissions int64
var (
_ sql.Scanner = (*Permissions)(nil)
_ driver.Value = Permissions(0)
_ fmt.Stringer = Permissions(0)
)
func (p Permissions) Has(perm ...Permissions) bool {
// Bitwise AND to compare if p has a permission
//
// If for example, p is 0x0010 ("edit.accessibility") and perm is
// 0x0001 ("read"): 0x0010 AND 0x0001 = 0x0000, which is not equal
// to 0x0001, return false.
//
// If p is 0x0011 ("edit.accessibility" and "read") and perm is
// 0x0001 ("read"): 0x0011 AND 0x0001 results in 0x0001, which
// is equal to 0x0001 ("read").
if len(perm) == 0 {
return false
}
if len(perm) == 1 {
return p&perm[0] == perm[0]
}
for _, pe := range perm {
if p&pe != pe {
return false
}
}
return true
}
func (p *Permissions) Add(perm ...Permissions) {
if p == nil {
t := Permissions(0)
p = &t
}
// Bitwise OR to add permissions.
//
// If p is 0x0001 ("read") and pe is 0x0010 ("edit.accessibility"):
// 0x0001 OR 0x0010 results in 0x0011, which means we added the "edit.accessibility" bit.
for _, pe := range perm {
*p = *p | pe
}
}
func (p *Permissions) Remove(perm ...Permissions) {
if p == nil {
return
}
// Bitwise NOT AND
//
// If p is 0x0011 ("read" + "edit.accessibility"), and perm is 0x0010 ("edit.accessibility"):
// we first convert perm to a bit-mask using NOT, so it becomes 0x1101; then we use AND to
// remove the "edit.accessibility", since 0x0011 AND 0x1101 results in 0x0001 ("read").
for _, pe := range perm {
*p = *p & (^pe)
}
}
func (p *Permissions) Scan(src any) error {
switch src := src.(type) {
case nil:
return nil
case int64:
*p = Permissions(src)
case string:
if strings.HasPrefix(src, "0x") {
i, err := strconv.ParseInt(strings.TrimPrefix(src, "0x"), 2, 64)
if err != nil {
return errors.Join(errors.New("Scan: unable to scan binary Permissions"), err)
}
return p.Scan(i)
}
i, err := strconv.ParseInt(src, 10, 64)
if err != nil {
return errors.Join(errors.New("Scan: unable to scan base10 Permissions"), err)
}
return p.Scan(i)
case []byte:
return p.Scan(string(src))
default:
return fmt.Errorf("Scan: unable to scan type %T into Permissions", src)
}
return nil
}
func (p Permissions) Value() (driver.Value, error) {
return int64(p), nil
}
func (p Permissions) String() string {
if p.Has(PermissionAuthor) {
return "author"
}
labels := []string{}
for perm, l := range PermissionLabels {
if p.Has(perm) {
labels = append(labels, l)
}
}
return strings.Join(labels, ",")
}
const (
PermissionAuthor Permissions = 0x1111111111111111 // "author"
PermissionAdminDelete Permissions = 0x1000000000000000 // "admin.delete" -----
PermissionAdminAll Permissions = 0x0111110000000001 // "admin.all"
PermissionAdminPublication Permissions = 0x0100000000000000 // "admin.publication"
PermissionAdminMembers Permissions = 0x0010000000000000 // "admin.members"
PermissionEditAll Permissions = 0x0000001111111111 // "edit.all" ---------
PermissionEditPages Permissions = 0x0000000100000000 // "edit.pages"
PermissionEditInteractions Permissions = 0x0000000010000000 // "edit.interactions"
PermissionEditDialogs Permissions = 0x0000000000001000 // "edit.dialogs"
PermissionEditTranslations Permissions = 0x0000000000000100 // "edit.translations"
PermissionEditAccessibility Permissions = 0x0000000000000010 // "edit.accessibility"
PermissionRead Permissions = 0x0000000000000001 // "read"
)
var PermissionLabels = map[Permissions]string{
PermissionAuthor: "author",
PermissionAdminDelete: "admin.delete",
PermissionAdminPublication: "admin.publication",
PermissionAdminMembers: "admin.members",
PermissionEditPages: "edit.pages",
PermissionEditInteractions: "edit.interactions",
PermissionEditDialogs: "edit.dialogs",
PermissionEditTranslations: "edit.translations",
PermissionEditAccessibility: "edit.accessibility",
PermissionRead: "read",
}