]> git.r.bdr.sh - rbdr/mobius/blobdiff - hotline/access.go
Improve human readability of account config files
[rbdr/mobius] / hotline / access.go
index 1bee72c00b4046c159e3a01b88768d00b24b68df..42e96c4bac5407027818b84cd8b1a06f9cb34edc 100644 (file)
 package hotline
 
-import (
-       "encoding/binary"
-       "math/big"
-)
+import "fmt"
 
 const (
-       // File System Maintenance
-       accessDeleteFile   = 0
-       accessUploadFile   = 1
-       accessDownloadFile = 2 // Can Download Files
-       accessRenameFile   = 3
-       accessMoveFile     = 4
-       accessCreateFolder = 5
-       accessDeleteFolder = 6
-       accessRenameFolder = 7
-       accessMoveFolder   = 8
-       accessReadChat     = 9
-       accessSendChat     = 10
-       accessOpenChat     = 11
-       // accessCloseChat        = 12 // Documented but unused?
-       // accessShowInList       = 13 // Documented but unused?
-       accessCreateUser = 14
-       accessDeleteUser = 15
-       accessOpenUser   = 16
-       accessModifyUser = 17
-       // accessChangeOwnPass    = 18 // Documented but unused?
-       // accessSendPrivMsg      = 19 // This doesn't do what it seems like it should do. TODO: Investigate
-       accessNewsReadArt    = 20
-       accessNewsPostArt    = 21
-       accessDisconUser     = 22 // Toggles red user name in user list
-       accessCannotBeDiscon = 23
-       accessGetClientInfo  = 24
-       accessUploadAnywhere = 25
-       accessAnyName        = 26
-       // accessNoAgreement      = 27
-       accessSetFileComment   = 28
-       accessSetFolderComment = 29
-       accessViewDropBoxes    = 30
-       accessMakeAlias        = 31
-       accessBroadcast        = 32
-       accessNewsDeleteArt    = 33
-       accessNewsCreateCat    = 34
-       // accessNewsDeleteCat    = 35
-       accessNewsCreateFldr = 36
-       // accessNewsDeleteFldr   = 37
+       AccessDeleteFile       = 0  // File System Maintenance: Can Delete Files
+       AccessUploadFile       = 1  // File System Maintenance: Can Upload Files
+       AccessDownloadFile     = 2  // File System Maintenance: Can Download Files
+       AccessRenameFile       = 3  // File System Maintenance: Can Rename Files
+       AccessMoveFile         = 4  // File System Maintenance: Can Move Files
+       AccessCreateFolder     = 5  // File System Maintenance: Can Create Folders
+       AccessDeleteFolder     = 6  // File System Maintenance: Can Delete Folders
+       AccessRenameFolder     = 7  // File System Maintenance: Can Rename Folders
+       AccessMoveFolder       = 8  // File System Maintenance: Can Move Folders
+       AccessReadChat         = 9  // Chat: Can Read Chat
+       AccessSendChat         = 10 // Chat: Can Send Chat
+       AccessOpenChat         = 11 // Chat: Can Initial Private Chat
+       AccessCloseChat        = 12 // Present in the Hotline 1.9 protocol documentation, but seemingly unused
+       AccessShowInList       = 13 // Present in the Hotline 1.9 protocol documentation, but seemingly unused
+       AccessCreateUser       = 14 // User Maintenance: Can Create Accounts
+       AccessDeleteUser       = 15 // User Maintenance: Can Delete Accounts
+       AccessOpenUser         = 16 // User Maintenance: Can Read Accounts
+       AccessModifyUser       = 17 // User Maintenance: Can Modify Accounts
+       AccessChangeOwnPass    = 18 // Present in the Hotline 1.9 protocol documentation, but seemingly unused
+       AccessNewsReadArt      = 20 // News: Can Read Articles
+       AccessNewsPostArt      = 21 // News: Can Post Articles
+       AccessDisconUser       = 22 // User Maintenance: Can Disconnect Users (Note: Turns username red in user list)
+       AccessCannotBeDiscon   = 23 // User Maintenance: Cannot be Disconnected
+       AccessGetClientInfo    = 24 // User Maintenance: Can Get User Info
+       AccessUploadAnywhere   = 25 // File System Maintenance: Can Upload Anywhere
+       AccessAnyName          = 26 // Miscellaneous: Can User Any Name
+       AccessNoAgreement      = 27 // Miscellaneous: Don't Show Agreement
+       AccessSetFileComment   = 28 // File System Maintenance: Can Comment Files
+       AccessSetFolderComment = 29 // File System Maintenance: Can Comment Folders
+       AccessViewDropBoxes    = 30 // File System Maintenance: Can View Drop Boxes
+       AccessMakeAlias        = 31 // File System Maintenance: Can Make Aliases
+       AccessBroadcast        = 32 // Messaging: Can Broadcast
+       AccessNewsDeleteArt    = 33 // News: Can Delete Articles
+       AccessNewsCreateCat    = 34 // News: Can Create Categories
+       AccessNewsDeleteCat    = 35 // News: Can Delete Categories
+       AccessNewsCreateFldr   = 36 // News: Can Create News Bundles
+       AccessNewsDeleteFldr   = 37 // News: Can Delete News Bundles
+       AccessUploadFolder     = 38 // File System Maintenance: Can Upload Folders
+       AccessDownloadFolder   = 39 // File System Maintenance: Can Download Folders
+       AccessSendPrivMsg      = 40 // Messaging: Can Send Messages (Note: 1.9 protocol doc incorrectly says this is bit 19)
 )
 
-type accessBitmap [8]byte
+type AccessBitmap [8]byte
 
-func (bits *accessBitmap) Set(i int) {
+func (bits *AccessBitmap) Set(i int) {
        bits[i/8] |= 1 << uint(7-i%8)
 }
 
-// authorize checks if 64 bit access slice contain has accessBit set
-// TODO: refactor to use accessBitmap type
-func authorize(access *[]byte, accessBit int) bool {
-       bits := big.NewInt(int64(binary.BigEndian.Uint64(*access)))
+func (bits *AccessBitmap) IsSet(i int) bool {
+       return bits[i/8]&(1<<uint(7-i%8)) != 0
+}
+
+func (bits *AccessBitmap) UnmarshalYAML(unmarshal func(interface{}) error) error {
+       var flags interface{}
+       err := unmarshal(&flags)
+       if err != nil {
+               return fmt.Errorf("unmarshal access bitmap: %w", err)
+       }
+
+       switch v := flags.(type) {
+       case []interface{}:
+               // Mobius versions < v0.17.0 store the user access bitmap as an array of int values like:
+               // [96, 112, 12, 32, 3, 128, 0, 0]
+               // This case supports reading of user config files using this format.
+               for i, v := range flags.([]interface{}) {
+                       bits[i] = byte(v.(int))
+               }
+       case map[string]interface{}:
+               // Mobius versions >= v0.17.0 store the user access bitmap as map[string]bool to provide a human-readable view of
+               // the account permissions.
+               if f, ok := v["DeleteFile"].(bool); ok && f {
+                       bits.Set(AccessDeleteFile)
+               }
+               if f, ok := v["UploadFile"].(bool); ok && f {
+                       bits.Set(AccessUploadFile)
+               }
+               if f, ok := v["DownloadFile"].(bool); ok && f {
+                       bits.Set(AccessDownloadFile)
+               }
+               if f, ok := v["UploadFolder"].(bool); ok && f {
+                       bits.Set(AccessUploadFolder)
+               }
+               if f, ok := v["DownloadFolder"].(bool); ok && f {
+                       bits.Set(AccessDownloadFolder)
+               }
+               if f, ok := v["RenameFile"].(bool); ok && f {
+                       bits.Set(AccessRenameFile)
+               }
+               if f, ok := v["MoveFile"].(bool); ok && f {
+                       bits.Set(AccessMoveFile)
+               }
+               if f, ok := v["CreateFolder"].(bool); ok && f {
+                       bits.Set(AccessCreateFolder)
+               }
+               if f, ok := v["DeleteFolder"].(bool); ok && f {
+                       bits.Set(AccessDeleteFolder)
+               }
+               if f, ok := v["RenameFolder"].(bool); ok && f {
+                       bits.Set(AccessRenameFolder)
+               }
+               if f, ok := v["MoveFolder"].(bool); ok && f {
+                       bits.Set(AccessMoveFolder)
+               }
+               if f, ok := v["ReadChat"].(bool); ok && f {
+                       bits.Set(AccessReadChat)
+               }
+               if f, ok := v["SendChat"].(bool); ok && f {
+                       bits.Set(AccessSendChat)
+               }
+               if f, ok := v["OpenChat"].(bool); ok && f {
+                       bits.Set(AccessOpenChat)
+               }
+               if f, ok := v["CloseChat"].(bool); ok && f {
+                       bits.Set(AccessCloseChat)
+               }
+               if f, ok := v["ShowInList"].(bool); ok && f {
+                       bits.Set(AccessShowInList)
+               }
+               if f, ok := v["CreateUser"].(bool); ok && f {
+                       bits.Set(AccessCreateUser)
+               }
+               if f, ok := v["DeleteUser"].(bool); ok && f {
+                       bits.Set(AccessDeleteUser)
+               }
+               if f, ok := v["OpenUser"].(bool); ok && f {
+                       bits.Set(AccessOpenUser)
+               }
+               if f, ok := v["ModifyUser"].(bool); ok && f {
+                       bits.Set(AccessModifyUser)
+               }
+               if f, ok := v["ChangeOwnPass"].(bool); ok && f {
+                       bits.Set(AccessChangeOwnPass)
+               }
+               if f, ok := v["NewsReadArt"].(bool); ok && f {
+                       bits.Set(AccessNewsReadArt)
+               }
+               if f, ok := v["NewsPostArt"].(bool); ok && f {
+                       bits.Set(AccessNewsPostArt)
+               }
+               if f, ok := v["DisconnectUser"].(bool); ok && f {
+                       bits.Set(AccessDisconUser)
+               }
+               if f, ok := v["CannotBeDisconnected"].(bool); ok && f {
+                       bits.Set(AccessCannotBeDiscon)
+               }
+               if f, ok := v["GetClientInfo"].(bool); ok && f {
+                       bits.Set(AccessGetClientInfo)
+               }
+               if f, ok := v["UploadAnywhere"].(bool); ok && f {
+                       bits.Set(AccessUploadAnywhere)
+               }
+               if f, ok := v["AnyName"].(bool); ok && f {
+                       bits.Set(AccessAnyName)
+               }
+               if f, ok := v["NoAgreement"].(bool); ok && f {
+                       bits.Set(AccessNoAgreement)
+               }
+               if f, ok := v["SetFileComment"].(bool); ok && f {
+                       bits.Set(AccessSetFileComment)
+               }
+               if f, ok := v["SetFolderComment"].(bool); ok && f {
+                       bits.Set(AccessSetFolderComment)
+               }
+               if f, ok := v["ViewDropBoxes"].(bool); ok && f {
+                       bits.Set(AccessViewDropBoxes)
+               }
+               if f, ok := v["MakeAlias"].(bool); ok && f {
+                       bits.Set(AccessMakeAlias)
+               }
+               if f, ok := v["Broadcast"].(bool); ok && f {
+                       bits.Set(AccessBroadcast)
+               }
+               if f, ok := v["NewsDeleteArt"].(bool); ok && f {
+                       bits.Set(AccessNewsDeleteArt)
+               }
+               if f, ok := v["NewsCreateCat"].(bool); ok && f {
+                       bits.Set(AccessNewsCreateCat)
+               }
+               if f, ok := v["NewsDeleteCat"].(bool); ok && f {
+                       bits.Set(AccessNewsDeleteCat)
+               }
+               if f, ok := v["NewsCreateFldr"].(bool); ok && f {
+                       bits.Set(AccessNewsCreateFldr)
+               }
+               if f, ok := v["NewsDeleteFldr"].(bool); ok && f {
+                       bits.Set(AccessNewsDeleteFldr)
+               }
+               if f, ok := v["SendPrivMsg"].(bool); ok && f {
+                       bits.Set(AccessSendPrivMsg)
+               }
+       }
+
+       return nil
+}
+
+// accessFlags is used to render the access bitmap to human-readable boolean flags in the account yaml.
+type accessFlags struct {
+       DownloadFile         bool `yaml:"DownloadFile"`
+       DownloadFolder       bool `yaml:"DownloadFolder"`
+       UploadFile           bool `yaml:"UploadFile"`
+       UploadFolder         bool `yaml:"UploadFolder"`
+       DeleteFile           bool `yaml:"DeleteFile"`
+       RenameFile           bool `yaml:"RenameFile"`
+       MoveFile             bool `yaml:"MoveFile"`
+       CreateFolder         bool `yaml:"CreateFolder"`
+       DeleteFolder         bool `yaml:"DeleteFolder"`
+       RenameFolder         bool `yaml:"RenameFolder"`
+       MoveFolder           bool `yaml:"MoveFolder"`
+       ReadChat             bool `yaml:"ReadChat"`
+       SendChat             bool `yaml:"SendChat"`
+       OpenChat             bool `yaml:"OpenChat"`
+       CloseChat            bool `yaml:"CloseChat"`
+       ShowInList           bool `yaml:"ShowInList"`
+       CreateUser           bool `yaml:"CreateUser"`
+       DeleteUser           bool `yaml:"DeleteUser"`
+       OpenUser             bool `yaml:"OpenUser"`
+       ModifyUser           bool `yaml:"ModifyUser"`
+       ChangeOwnPass        bool `yaml:"ChangeOwnPass"`
+       NewsReadArt          bool `yaml:"NewsReadArt"`
+       NewsPostArt          bool `yaml:"NewsPostArt"`
+       DisconnectUser       bool `yaml:"DisconnectUser"`
+       CannotBeDisconnected bool `yaml:"CannotBeDisconnected"`
+       GetClientInfo        bool `yaml:"GetClientInfo"`
+       UploadAnywhere       bool `yaml:"UploadAnywhere"`
+       AnyName              bool `yaml:"AnyName"`
+       NoAgreement          bool `yaml:"NoAgreement"`
+       SetFileComment       bool `yaml:"SetFileComment"`
+       SetFolderComment     bool `yaml:"SetFolderComment"`
+       ViewDropBoxes        bool `yaml:"ViewDropBoxes"`
+       MakeAlias            bool `yaml:"MakeAlias"`
+       Broadcast            bool `yaml:"Broadcast"`
+       NewsDeleteArt        bool `yaml:"NewsDeleteArt"`
+       NewsCreateCat        bool `yaml:"NewsCreateCat"`
+       NewsDeleteCat        bool `yaml:"NewsDeleteCat"`
+       NewsCreateFldr       bool `yaml:"NewsCreateFldr"`
+       NewsDeleteFldr       bool `yaml:"NewsDeleteFldr"`
+       SendPrivMsg          bool `yaml:"SendPrivMsg"`
+}
 
-       return bits.Bit(63-accessBit) == 1
+func (bits AccessBitmap) MarshalYAML() (interface{}, error) {
+       return accessFlags{
+               DownloadFile:         bits.IsSet(AccessDownloadFile),
+               DownloadFolder:       bits.IsSet(AccessDownloadFolder),
+               UploadFolder:         bits.IsSet(AccessUploadFolder),
+               DeleteFile:           bits.IsSet(AccessDeleteFile),
+               UploadFile:           bits.IsSet(AccessUploadFile),
+               RenameFile:           bits.IsSet(AccessRenameFile),
+               MoveFile:             bits.IsSet(AccessMoveFile),
+               CreateFolder:         bits.IsSet(AccessCreateFolder),
+               DeleteFolder:         bits.IsSet(AccessDeleteFolder),
+               RenameFolder:         bits.IsSet(AccessRenameFolder),
+               MoveFolder:           bits.IsSet(AccessMoveFolder),
+               ReadChat:             bits.IsSet(AccessReadChat),
+               SendChat:             bits.IsSet(AccessSendChat),
+               OpenChat:             bits.IsSet(AccessOpenChat),
+               CloseChat:            bits.IsSet(AccessCloseChat),
+               ShowInList:           bits.IsSet(AccessShowInList),
+               CreateUser:           bits.IsSet(AccessCreateUser),
+               DeleteUser:           bits.IsSet(AccessDeleteUser),
+               OpenUser:             bits.IsSet(AccessOpenUser),
+               ModifyUser:           bits.IsSet(AccessModifyUser),
+               ChangeOwnPass:        bits.IsSet(AccessChangeOwnPass),
+               NewsReadArt:          bits.IsSet(AccessNewsReadArt),
+               NewsPostArt:          bits.IsSet(AccessNewsPostArt),
+               DisconnectUser:       bits.IsSet(AccessDisconUser),
+               CannotBeDisconnected: bits.IsSet(AccessCannotBeDiscon),
+               GetClientInfo:        bits.IsSet(AccessGetClientInfo),
+               UploadAnywhere:       bits.IsSet(AccessUploadAnywhere),
+               AnyName:              bits.IsSet(AccessAnyName),
+               NoAgreement:          bits.IsSet(AccessNoAgreement),
+               SetFileComment:       bits.IsSet(AccessSetFileComment),
+               SetFolderComment:     bits.IsSet(AccessSetFolderComment),
+               ViewDropBoxes:        bits.IsSet(AccessViewDropBoxes),
+               MakeAlias:            bits.IsSet(AccessMakeAlias),
+               Broadcast:            bits.IsSet(AccessBroadcast),
+               NewsDeleteArt:        bits.IsSet(AccessNewsDeleteArt),
+               NewsCreateCat:        bits.IsSet(AccessNewsCreateCat),
+               NewsDeleteCat:        bits.IsSet(AccessNewsDeleteCat),
+               NewsCreateFldr:       bits.IsSet(AccessNewsCreateFldr),
+               NewsDeleteFldr:       bits.IsSet(AccessNewsDeleteFldr),
+               SendPrivMsg:          bits.IsSet(AccessSendPrivMsg),
+       }, nil
 }