mirror of
https://github.com/binwiederhier/ntfy.git
synced 2025-07-20 10:04:08 +00:00
Replace read/write flags with Permission
This commit is contained in:
parent
1733323132
commit
4b9d40464c
13 changed files with 194 additions and 152 deletions
|
@ -182,8 +182,7 @@ const (
|
|||
// in a SQLite database.
|
||||
type Manager struct {
|
||||
db *sql.DB
|
||||
defaultRead bool // Default read permission if no ACL matches
|
||||
defaultWrite bool // Default write permission if no ACL matches
|
||||
defaultAccess Permission // Default permission if no ACL matches
|
||||
statsQueue map[string]*User // Username -> User, for "unimportant" user updates
|
||||
tokenExpiryInterval time.Duration // Duration after which tokens expire, and by which tokens are extended
|
||||
mu sync.Mutex
|
||||
|
@ -192,12 +191,12 @@ type Manager struct {
|
|||
var _ Auther = (*Manager)(nil)
|
||||
|
||||
// NewManager creates a new Manager instance
|
||||
func NewManager(filename string, defaultRead, defaultWrite bool) (*Manager, error) {
|
||||
return newManager(filename, defaultRead, defaultWrite, userTokenExpiryDuration, userStatsQueueWriterInterval)
|
||||
func NewManager(filename string, defaultAccess Permission) (*Manager, error) {
|
||||
return newManager(filename, defaultAccess, userTokenExpiryDuration, userStatsQueueWriterInterval)
|
||||
}
|
||||
|
||||
// NewManager creates a new Manager instance
|
||||
func newManager(filename string, defaultRead, defaultWrite bool, tokenExpiryDuration, statsWriterInterval time.Duration) (*Manager, error) {
|
||||
func newManager(filename string, defaultAccess Permission, tokenExpiryDuration, statsWriterInterval time.Duration) (*Manager, error) {
|
||||
db, err := sql.Open("sqlite3", filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -207,8 +206,7 @@ func newManager(filename string, defaultRead, defaultWrite bool, tokenExpiryDura
|
|||
}
|
||||
manager := &Manager{
|
||||
db: db,
|
||||
defaultRead: defaultRead,
|
||||
defaultWrite: defaultWrite,
|
||||
defaultAccess: defaultAccess,
|
||||
statsQueue: make(map[string]*User),
|
||||
tokenExpiryInterval: tokenExpiryDuration,
|
||||
}
|
||||
|
@ -368,7 +366,7 @@ func (a *Manager) Authorize(user *User, topic string, perm Permission) error {
|
|||
}
|
||||
defer rows.Close()
|
||||
if !rows.Next() {
|
||||
return a.resolvePerms(a.defaultRead, a.defaultWrite, perm)
|
||||
return a.resolvePerms(a.defaultAccess, perm)
|
||||
}
|
||||
var read, write bool
|
||||
if err := rows.Scan(&read, &write); err != nil {
|
||||
|
@ -376,13 +374,13 @@ func (a *Manager) Authorize(user *User, topic string, perm Permission) error {
|
|||
} else if err := rows.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
return a.resolvePerms(read, write, perm)
|
||||
return a.resolvePerms(NewPermission(read, write), perm)
|
||||
}
|
||||
|
||||
func (a *Manager) resolvePerms(read, write bool, perm Permission) error {
|
||||
if perm == PermissionRead && read {
|
||||
func (a *Manager) resolvePerms(base, perm Permission) error {
|
||||
if perm == PermissionRead && base.IsRead() {
|
||||
return nil
|
||||
} else if perm == PermissionWrite && write {
|
||||
} else if perm == PermissionWrite && base.IsWrite() {
|
||||
return nil
|
||||
}
|
||||
return ErrUnauthorized
|
||||
|
@ -534,8 +532,7 @@ func (a *Manager) Grants(username string) ([]Grant, error) {
|
|||
}
|
||||
grants = append(grants, Grant{
|
||||
TopicPattern: fromSQLWildcard(topic),
|
||||
AllowRead: read,
|
||||
AllowWrite: write,
|
||||
Allow: NewPermission(read, write),
|
||||
})
|
||||
}
|
||||
return grants, nil
|
||||
|
@ -551,19 +548,17 @@ func (a *Manager) Reservations(username string) ([]Reservation, error) {
|
|||
reservations := make([]Reservation, 0)
|
||||
for rows.Next() {
|
||||
var topic string
|
||||
var read, write bool
|
||||
var ownerRead, ownerWrite bool
|
||||
var everyoneRead, everyoneWrite sql.NullBool
|
||||
if err := rows.Scan(&topic, &read, &write, &everyoneRead, &everyoneWrite); err != nil {
|
||||
if err := rows.Scan(&topic, &ownerRead, &ownerWrite, &everyoneRead, &everyoneWrite); err != nil {
|
||||
return nil, err
|
||||
} else if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reservations = append(reservations, Reservation{
|
||||
TopicPattern: topic,
|
||||
AllowRead: read,
|
||||
AllowWrite: write,
|
||||
AllowEveryoneRead: everyoneRead.Bool, // false if null
|
||||
AllowEveryoneWrite: everyoneWrite.Bool, // false if null
|
||||
Topic: topic,
|
||||
Owner: NewPermission(ownerRead, ownerWrite),
|
||||
Everyone: NewPermission(everyoneRead.Bool, everyoneWrite.Bool), // false if null
|
||||
})
|
||||
}
|
||||
return reservations, nil
|
||||
|
@ -659,8 +654,8 @@ func (a *Manager) ResetAccess(username string, topicPattern string) error {
|
|||
}
|
||||
|
||||
// DefaultAccess returns the default read/write access if no access control entry matches
|
||||
func (a *Manager) DefaultAccess() (read bool, write bool) {
|
||||
return a.defaultRead, a.defaultWrite
|
||||
func (a *Manager) DefaultAccess() Permission {
|
||||
return a.defaultAccess
|
||||
}
|
||||
|
||||
func toSQLWildcard(s string) string {
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
const minBcryptTimingMillis = int64(50) // Ideally should be >100ms, but this should also run on a Raspberry Pi without massive resources
|
||||
|
||||
func TestManager_FullScenario_Default_DenyAll(t *testing.T) {
|
||||
a := newTestManager(t, false, false)
|
||||
a := newTestManager(t, PermissionDenyAll)
|
||||
require.Nil(t, a.AddUser("phil", "phil", RoleAdmin))
|
||||
require.Nil(t, a.AddUser("ben", "ben", RoleUser))
|
||||
require.Nil(t, a.AllowAccess("", "ben", "mytopic", true, true))
|
||||
|
@ -28,19 +28,25 @@ func TestManager_FullScenario_Default_DenyAll(t *testing.T) {
|
|||
require.Equal(t, "phil", phil.Name)
|
||||
require.True(t, strings.HasPrefix(phil.Hash, "$2a$10$"))
|
||||
require.Equal(t, RoleAdmin, phil.Role)
|
||||
require.Equal(t, []Grant{}, phil.Grants)
|
||||
|
||||
philGrants, err := a.Grants("phil")
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, []Grant{}, philGrants)
|
||||
|
||||
ben, err := a.Authenticate("ben", "ben")
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "ben", ben.Name)
|
||||
require.True(t, strings.HasPrefix(ben.Hash, "$2a$10$"))
|
||||
require.Equal(t, RoleUser, ben.Role)
|
||||
|
||||
benGrants, err := a.Grants("ben")
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, []Grant{
|
||||
{"mytopic", true, true, false},
|
||||
{"writeme", false, true, false},
|
||||
{"readme", true, false, false},
|
||||
{"everyonewrite", false, false, false},
|
||||
}, ben.Grants)
|
||||
{"mytopic", PermissionReadWrite},
|
||||
{"writeme", PermissionWrite},
|
||||
{"readme", PermissionRead},
|
||||
{"everyonewrite", PermissionDenyAll},
|
||||
}, benGrants)
|
||||
|
||||
notben, err := a.Authenticate("ben", "this is wrong")
|
||||
require.Nil(t, notben)
|
||||
|
@ -85,20 +91,20 @@ func TestManager_FullScenario_Default_DenyAll(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestManager_AddUser_Invalid(t *testing.T) {
|
||||
a := newTestManager(t, false, false)
|
||||
a := newTestManager(t, PermissionDenyAll)
|
||||
require.Equal(t, ErrInvalidArgument, a.AddUser(" invalid ", "pass", RoleAdmin))
|
||||
require.Equal(t, ErrInvalidArgument, a.AddUser("validuser", "pass", "invalid-role"))
|
||||
}
|
||||
|
||||
func TestManager_AddUser_Timing(t *testing.T) {
|
||||
a := newTestManager(t, false, false)
|
||||
a := newTestManager(t, PermissionDenyAll)
|
||||
start := time.Now().UnixMilli()
|
||||
require.Nil(t, a.AddUser("user", "pass", RoleAdmin))
|
||||
require.GreaterOrEqual(t, time.Now().UnixMilli()-start, minBcryptTimingMillis)
|
||||
}
|
||||
|
||||
func TestManager_Authenticate_Timing(t *testing.T) {
|
||||
a := newTestManager(t, false, false)
|
||||
a := newTestManager(t, PermissionDenyAll)
|
||||
require.Nil(t, a.AddUser("user", "pass", RoleAdmin))
|
||||
|
||||
// Timing a correct attempt
|
||||
|
@ -121,7 +127,7 @@ func TestManager_Authenticate_Timing(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestManager_UserManagement(t *testing.T) {
|
||||
a := newTestManager(t, false, false)
|
||||
a := newTestManager(t, PermissionDenyAll)
|
||||
require.Nil(t, a.AddUser("phil", "phil", RoleAdmin))
|
||||
require.Nil(t, a.AddUser("ben", "ben", RoleUser))
|
||||
require.Nil(t, a.AllowAccess("", "ben", "mytopic", true, true))
|
||||
|
@ -137,29 +143,38 @@ func TestManager_UserManagement(t *testing.T) {
|
|||
require.Equal(t, "phil", phil.Name)
|
||||
require.True(t, strings.HasPrefix(phil.Hash, "$2a$10$"))
|
||||
require.Equal(t, RoleAdmin, phil.Role)
|
||||
require.Equal(t, []Grant{}, phil.Grants)
|
||||
|
||||
philGrants, err := a.Grants("phil")
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, []Grant{}, philGrants)
|
||||
|
||||
ben, err := a.User("ben")
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "ben", ben.Name)
|
||||
require.True(t, strings.HasPrefix(ben.Hash, "$2a$10$"))
|
||||
require.Equal(t, RoleUser, ben.Role)
|
||||
|
||||
benGrants, err := a.Grants("ben")
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, []Grant{
|
||||
{"mytopic", true, true, false},
|
||||
{"writeme", false, true, false},
|
||||
{"readme", true, false, false},
|
||||
{"everyonewrite", false, false, false},
|
||||
}, ben.Grants)
|
||||
{"mytopic", PermissionReadWrite},
|
||||
{"writeme", PermissionWrite},
|
||||
{"readme", PermissionRead},
|
||||
{"everyonewrite", PermissionDenyAll},
|
||||
}, benGrants)
|
||||
|
||||
everyone, err := a.User(Everyone)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, "*", everyone.Name)
|
||||
require.Equal(t, "", everyone.Hash)
|
||||
require.Equal(t, RoleAnonymous, everyone.Role)
|
||||
|
||||
everyoneGrants, err := a.Grants(Everyone)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, []Grant{
|
||||
{"everyonewrite", true, true, false},
|
||||
{"announcements", true, false, false},
|
||||
}, everyone.Grants)
|
||||
{"everyonewrite", PermissionReadWrite},
|
||||
{"announcements", PermissionRead},
|
||||
}, everyoneGrants)
|
||||
|
||||
// Ben: Before revoking
|
||||
require.Nil(t, a.AllowAccess("", "ben", "mytopic", true, true)) // Overwrite!
|
||||
|
@ -203,7 +218,7 @@ func TestManager_UserManagement(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestManager_ChangePassword(t *testing.T) {
|
||||
a := newTestManager(t, false, false)
|
||||
a := newTestManager(t, PermissionDenyAll)
|
||||
require.Nil(t, a.AddUser("phil", "phil", RoleAdmin))
|
||||
|
||||
_, err := a.Authenticate("phil", "phil")
|
||||
|
@ -217,7 +232,7 @@ func TestManager_ChangePassword(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestManager_ChangeRole(t *testing.T) {
|
||||
a := newTestManager(t, false, false)
|
||||
a := newTestManager(t, PermissionDenyAll)
|
||||
require.Nil(t, a.AddUser("ben", "ben", RoleUser))
|
||||
require.Nil(t, a.AllowAccess("", "ben", "mytopic", true, true))
|
||||
require.Nil(t, a.AllowAccess("", "ben", "readme", true, false))
|
||||
|
@ -225,18 +240,24 @@ func TestManager_ChangeRole(t *testing.T) {
|
|||
ben, err := a.User("ben")
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, RoleUser, ben.Role)
|
||||
require.Equal(t, 2, len(ben.Grants))
|
||||
|
||||
benGrants, err := a.Grants("ben")
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 2, len(benGrants))
|
||||
|
||||
require.Nil(t, a.ChangeRole("ben", RoleAdmin))
|
||||
|
||||
ben, err = a.User("ben")
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, RoleAdmin, ben.Role)
|
||||
require.Equal(t, 0, len(ben.Grants))
|
||||
|
||||
benGrants, err = a.Grants("ben")
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 0, len(benGrants))
|
||||
}
|
||||
|
||||
func TestManager_Token_Valid(t *testing.T) {
|
||||
a := newTestManager(t, false, false)
|
||||
a := newTestManager(t, PermissionDenyAll)
|
||||
require.Nil(t, a.AddUser("ben", "ben", RoleUser))
|
||||
|
||||
u, err := a.User("ben")
|
||||
|
@ -261,7 +282,7 @@ func TestManager_Token_Valid(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestManager_Token_Invalid(t *testing.T) {
|
||||
a := newTestManager(t, false, false)
|
||||
a := newTestManager(t, PermissionDenyAll)
|
||||
require.Nil(t, a.AddUser("ben", "ben", RoleUser))
|
||||
|
||||
u, err := a.AuthenticateToken(strings.Repeat("x", 32)) // 32 == token length
|
||||
|
@ -274,7 +295,7 @@ func TestManager_Token_Invalid(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestManager_Token_Expire(t *testing.T) {
|
||||
a := newTestManager(t, false, false)
|
||||
a := newTestManager(t, PermissionDenyAll)
|
||||
require.Nil(t, a.AddUser("ben", "ben", RoleUser))
|
||||
|
||||
u, err := a.User("ben")
|
||||
|
@ -322,7 +343,7 @@ func TestManager_Token_Expire(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestManager_Token_Extend(t *testing.T) {
|
||||
a := newTestManager(t, false, false)
|
||||
a := newTestManager(t, PermissionDenyAll)
|
||||
require.Nil(t, a.AddUser("ben", "ben", RoleUser))
|
||||
|
||||
// Try to extend token for user without token
|
||||
|
@ -349,7 +370,7 @@ func TestManager_Token_Extend(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestManager_EnqueueStats(t *testing.T) {
|
||||
a, err := newManager(filepath.Join(t.TempDir(), "db"), true, true, time.Hour, 1500*time.Millisecond)
|
||||
a, err := newManager(filepath.Join(t.TempDir(), "db"), PermissionReadWrite, time.Hour, 1500*time.Millisecond)
|
||||
require.Nil(t, err)
|
||||
require.Nil(t, a.AddUser("ben", "ben", RoleUser))
|
||||
|
||||
|
@ -379,7 +400,7 @@ func TestManager_EnqueueStats(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestManager_ChangeSettings(t *testing.T) {
|
||||
a, err := newManager(filepath.Join(t.TempDir(), "db"), true, true, time.Hour, 1500*time.Millisecond)
|
||||
a, err := newManager(filepath.Join(t.TempDir(), "db"), PermissionReadWrite, time.Hour, 1500*time.Millisecond)
|
||||
require.Nil(t, err)
|
||||
require.Nil(t, a.AddUser("ben", "ben", RoleUser))
|
||||
|
||||
|
@ -461,7 +482,7 @@ func TestSqliteCache_Migration_From1(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
|
||||
// Create manager to trigger migration
|
||||
a := newTestManagerFromFile(t, filename, false, false, userTokenExpiryDuration, userStatsQueueWriterInterval)
|
||||
a := newTestManagerFromFile(t, filename, PermissionDenyAll, userTokenExpiryDuration, userStatsQueueWriterInterval)
|
||||
checkSchemaVersion(t, a.db)
|
||||
|
||||
users, err := a.Users()
|
||||
|
@ -469,26 +490,32 @@ func TestSqliteCache_Migration_From1(t *testing.T) {
|
|||
require.Equal(t, 3, len(users))
|
||||
phil, ben, everyone := users[0], users[1], users[2]
|
||||
|
||||
philGrants, err := a.Grants("phil")
|
||||
require.Nil(t, err)
|
||||
|
||||
benGrants, err := a.Grants("ben")
|
||||
require.Nil(t, err)
|
||||
|
||||
everyoneGrants, err := a.Grants(Everyone)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, "phil", phil.Name)
|
||||
require.Equal(t, RoleAdmin, phil.Role)
|
||||
require.Equal(t, 0, len(phil.Grants))
|
||||
require.Equal(t, 0, len(philGrants))
|
||||
|
||||
require.Equal(t, "ben", ben.Name)
|
||||
require.Equal(t, RoleUser, ben.Role)
|
||||
require.Equal(t, 2, len(ben.Grants))
|
||||
require.Equal(t, "stats", ben.Grants[0].TopicPattern)
|
||||
require.Equal(t, true, ben.Grants[0].AllowRead)
|
||||
require.Equal(t, true, ben.Grants[0].AllowWrite)
|
||||
require.Equal(t, "secret", ben.Grants[1].TopicPattern)
|
||||
require.Equal(t, true, ben.Grants[1].AllowRead)
|
||||
require.Equal(t, false, ben.Grants[1].AllowWrite)
|
||||
require.Equal(t, 2, len(benGrants))
|
||||
require.Equal(t, "stats", benGrants[0].TopicPattern)
|
||||
require.Equal(t, PermissionReadWrite, benGrants[0].Allow)
|
||||
require.Equal(t, "secret", benGrants[1].TopicPattern)
|
||||
require.Equal(t, PermissionRead, benGrants[1].Allow)
|
||||
|
||||
require.Equal(t, Everyone, everyone.Name)
|
||||
require.Equal(t, RoleAnonymous, everyone.Role)
|
||||
require.Equal(t, 1, len(everyone.Grants))
|
||||
require.Equal(t, "stats", everyone.Grants[0].TopicPattern)
|
||||
require.Equal(t, true, everyone.Grants[0].AllowRead)
|
||||
require.Equal(t, false, everyone.Grants[0].AllowWrite)
|
||||
require.Equal(t, 1, len(everyoneGrants))
|
||||
require.Equal(t, "stats", everyoneGrants[0].TopicPattern)
|
||||
require.Equal(t, PermissionRead, everyoneGrants[0].Allow)
|
||||
}
|
||||
|
||||
func checkSchemaVersion(t *testing.T, db *sql.DB) {
|
||||
|
@ -502,12 +529,12 @@ func checkSchemaVersion(t *testing.T, db *sql.DB) {
|
|||
require.Nil(t, rows.Close())
|
||||
}
|
||||
|
||||
func newTestManager(t *testing.T, defaultRead, defaultWrite bool) *Manager {
|
||||
return newTestManagerFromFile(t, filepath.Join(t.TempDir(), "user.db"), defaultRead, defaultWrite, userTokenExpiryDuration, userStatsQueueWriterInterval)
|
||||
func newTestManager(t *testing.T, defaultAccess Permission) *Manager {
|
||||
return newTestManagerFromFile(t, filepath.Join(t.TempDir(), "user.db"), defaultAccess, userTokenExpiryDuration, userStatsQueueWriterInterval)
|
||||
}
|
||||
|
||||
func newTestManagerFromFile(t *testing.T, filename string, defaultRead, defaultWrite bool, tokenExpiryDuration, statsWriterInterval time.Duration) *Manager {
|
||||
a, err := newManager(filename, defaultRead, defaultWrite, tokenExpiryDuration, statsWriterInterval)
|
||||
func newTestManagerFromFile(t *testing.T, filename string, defaultAccess Permission, tokenExpiryDuration, statsWriterInterval time.Duration) *Manager {
|
||||
a, err := newManager(filename, defaultAccess, tokenExpiryDuration, statsWriterInterval)
|
||||
require.Nil(t, err)
|
||||
return a
|
||||
}
|
||||
|
|
|
@ -85,31 +85,79 @@ type Stats struct {
|
|||
Emails int64
|
||||
}
|
||||
|
||||
// Grant is a struct that represents an access control entry to a topic
|
||||
// Grant is a struct that represents an access control entry to a topic by a user
|
||||
type Grant struct {
|
||||
TopicPattern string // May include wildcard (*)
|
||||
AllowRead bool
|
||||
AllowWrite bool
|
||||
Allow Permission
|
||||
}
|
||||
|
||||
// Reservation is a struct that represents the ownership over a topic by a user
|
||||
type Reservation struct {
|
||||
TopicPattern string
|
||||
AllowRead bool
|
||||
AllowWrite bool
|
||||
AllowEveryoneRead bool
|
||||
AllowEveryoneWrite bool
|
||||
Topic string
|
||||
Owner Permission
|
||||
Everyone Permission
|
||||
}
|
||||
|
||||
// Permission represents a read or write permission to a topic
|
||||
type Permission int
|
||||
type Permission uint8
|
||||
|
||||
// Permissions to a topic
|
||||
const (
|
||||
PermissionRead = Permission(1)
|
||||
PermissionWrite = Permission(2)
|
||||
PermissionDenyAll Permission = iota
|
||||
PermissionRead
|
||||
PermissionWrite
|
||||
PermissionReadWrite // 3!
|
||||
)
|
||||
|
||||
func NewPermission(read, write bool) Permission {
|
||||
p := uint8(0)
|
||||
if read {
|
||||
p |= uint8(PermissionRead)
|
||||
}
|
||||
if write {
|
||||
p |= uint8(PermissionWrite)
|
||||
}
|
||||
return Permission(p)
|
||||
}
|
||||
|
||||
func ParsePermission(s string) (Permission, error) {
|
||||
switch s {
|
||||
case "read-write", "rw":
|
||||
return NewPermission(true, true), nil
|
||||
case "read-only", "read", "ro":
|
||||
return NewPermission(true, false), nil
|
||||
case "write-only", "write", "wo":
|
||||
return NewPermission(false, true), nil
|
||||
case "deny-all", "deny", "none":
|
||||
return NewPermission(false, false), nil
|
||||
default:
|
||||
return NewPermission(false, false), errors.New("invalid permission")
|
||||
}
|
||||
}
|
||||
|
||||
func (p Permission) IsRead() bool {
|
||||
return p&PermissionRead != 0
|
||||
}
|
||||
|
||||
func (p Permission) IsWrite() bool {
|
||||
return p&PermissionWrite != 0
|
||||
}
|
||||
|
||||
func (p Permission) IsReadWrite() bool {
|
||||
return p.IsRead() && p.IsWrite()
|
||||
}
|
||||
|
||||
func (p Permission) String() string {
|
||||
if p.IsReadWrite() {
|
||||
return "read-write"
|
||||
} else if p.IsRead() {
|
||||
return "read-only"
|
||||
} else if p.IsWrite() {
|
||||
return "write-only"
|
||||
}
|
||||
return "deny-all"
|
||||
}
|
||||
|
||||
// Role represents a user's role, either admin or regular user
|
||||
type Role string
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue