aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/mobius-hotline-server/mobius/config/Users/admin.yaml49
-rw-r--r--cmd/mobius-hotline-server/mobius/config/Users/guest.yaml45
-rw-r--r--hotline/access.go236
-rw-r--r--hotline/account.go2
-rw-r--r--internal/mobius/account_manager.go3
-rw-r--r--internal/mobius/account_manager_test.go89
-rw-r--r--internal/mobius/test/config/Users/admin.yaml52
-rw-r--r--internal/mobius/test/config/Users/guest.yaml51
-rw-r--r--internal/mobius/test/config/Users/user-with-old-access-format.yaml12
-rw-r--r--internal/mobius/transaction_handlers.go6
-rw-r--r--internal/mobius/transaction_handlers_test.go94
11 files changed, 606 insertions, 33 deletions
diff --git a/cmd/mobius-hotline-server/mobius/config/Users/admin.yaml b/cmd/mobius-hotline-server/mobius/config/Users/admin.yaml
index 5413735..1e72e6b 100644
--- a/cmd/mobius-hotline-server/mobius/config/Users/admin.yaml
+++ b/cmd/mobius-hotline-server/mobius/config/Users/admin.yaml
@@ -2,11 +2,44 @@ Login: admin
Name: admin
Password: $2a$04$2itGEYx8C1N5bsfRSoC9JuonS3I4YfnyVPZHLSwp7kEInRX0yoB.a
Access:
-- 255
-- 255
-- 255
-- 255
-- 255
-- 255
-- 255
-- 255
+ DownloadFile: true
+ DownloadFolder: true
+ UploadFile: true
+ UploadFolder: true
+ DeleteFile: true
+ RenameFile: true
+ MoveFile: true
+ CreateFolder: true
+ DeleteFolder: true
+ RenameFolder: true
+ MoveFolder: true
+ ReadChat: true
+ SendChat: true
+ OpenChat: true
+ CloseChat: true
+ ShowInList: true
+ CreateUser: true
+ DeleteUser: true
+ OpenUser: true
+ ModifyUser: true
+ ChangeOwnPass: true
+ NewsReadArt: true
+ NewsPostArt: true
+ DisconnectUser: true
+ CannotBeDisconnected: true
+ GetClientInfo: true
+ UploadAnywhere: true
+ AnyName: true
+ NoAgreement: true
+ SetFileComment: true
+ SetFolderComment: true
+ ViewDropBoxes: true
+ MakeAlias: true
+ Broadcast: false
+ NewsDeleteArt: true
+ NewsCreateCat: true
+ NewsDeleteCat: true
+ NewsCreateFldr: true
+ NewsDeleteFldr: true
+ SendPrivMsg: true
+FileRoot: ""
diff --git a/cmd/mobius-hotline-server/mobius/config/Users/guest.yaml b/cmd/mobius-hotline-server/mobius/config/Users/guest.yaml
index bacffee..86235d1 100644
--- a/cmd/mobius-hotline-server/mobius/config/Users/guest.yaml
+++ b/cmd/mobius-hotline-server/mobius/config/Users/guest.yaml
@@ -1,4 +1,45 @@
Login: guest
Name: guest
-Password: $2a$04$9P/jgLn1fR9TjSoWL.rKxuN6g.1TSpf2o6Hw.aaRuBwrWIJNwsKkS
-Access: [96, 112, 12, 32, 3, 128, 0, 0]
+Password: $2a$04$6Yq/TIlgjSD.FbARwtYs9ODnkHawonu1TJ5W2jJKfhnHwBIQTk./y
+Access:
+ DownloadFile: true
+ DownloadFolder: true
+ UploadFile: true
+ UploadFolder: true
+ DeleteFile: false
+ RenameFile: false
+ MoveFile: false
+ CreateFolder: false
+ DeleteFolder: false
+ RenameFolder: false
+ MoveFolder: false
+ ReadChat: true
+ SendChat: true
+ OpenChat: true
+ CloseChat: false
+ ShowInList: false
+ CreateUser: false
+ DeleteUser: false
+ OpenUser: false
+ ModifyUser: false
+ ChangeOwnPass: false
+ NewsReadArt: true
+ NewsPostArt: true
+ DisconnectUser: false
+ CannotBeDisconnected: false
+ GetClientInfo: false
+ UploadAnywhere: false
+ AnyName: true
+ NoAgreement: false
+ SetFileComment: false
+ SetFolderComment: false
+ ViewDropBoxes: false
+ MakeAlias: false
+ Broadcast: false
+ NewsDeleteArt: false
+ NewsCreateCat: false
+ NewsDeleteCat: false
+ NewsCreateFldr: false
+ NewsDeleteFldr: false
+ SendPrivMsg: true
+FileRoot: ""
diff --git a/hotline/access.go b/hotline/access.go
index 2d78464..42e96c4 100644
--- a/hotline/access.go
+++ b/hotline/access.go
@@ -1,5 +1,7 @@
package hotline
+import "fmt"
+
const (
AccessDeleteFile = 0 // File System Maintenance: Can Delete Files
AccessUploadFile = 1 // File System Maintenance: Can Upload Files
@@ -38,6 +40,8 @@ const (
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)
)
@@ -50,3 +54,235 @@ func (bits *AccessBitmap) Set(i int) {
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"`
+}
+
+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
+}
diff --git a/hotline/account.go b/hotline/account.go
index 526eb05..39ea974 100644
--- a/hotline/account.go
+++ b/hotline/account.go
@@ -14,7 +14,7 @@ type Account struct {
Login string `yaml:"Login"`
Name string `yaml:"Name"`
Password string `yaml:"Password"`
- Access AccessBitmap `yaml:"Access,flow"`
+ Access AccessBitmap `yaml:"Access"`
FileRoot string `yaml:"FileRoot"`
readOffset int // Internal offset to track read progress
diff --git a/internal/mobius/account_manager.go b/internal/mobius/account_manager.go
index cba33b2..3bb5cbc 100644
--- a/internal/mobius/account_manager.go
+++ b/internal/mobius/account_manager.go
@@ -38,7 +38,7 @@ func NewYAMLAccountManager(accountDir string) (*YAMLAccountManager, error) {
matches, err := filepath.Glob(filepath.Join(accountDir, "*.yaml"))
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("list account files: %w", err)
}
if len(matches) == 0 {
@@ -50,7 +50,6 @@ func NewYAMLAccountManager(accountDir string) (*YAMLAccountManager, error) {
if err = loadFromYAMLFile(file, &account); err != nil {
return nil, fmt.Errorf("error loading account %s: %w", file, err)
}
-
accountMgr.accounts[account.Login] = account
}
diff --git a/internal/mobius/account_manager_test.go b/internal/mobius/account_manager_test.go
new file mode 100644
index 0000000..fe834a4
--- /dev/null
+++ b/internal/mobius/account_manager_test.go
@@ -0,0 +1,89 @@
+package mobius
+
+import (
+ "fmt"
+ "github.com/jhalter/mobius/hotline"
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestNewYAMLAccountManager(t *testing.T) {
+ type args struct {
+ accountDir string
+ }
+ tests := []struct {
+ name string
+ args args
+ want *YAMLAccountManager
+ wantErr assert.ErrorAssertionFunc
+ }{
+ {
+ name: "loads accounts from a directory",
+ args: args{
+ accountDir: "test/config/Users",
+ },
+ want: &YAMLAccountManager{
+ accountDir: "test/config/Users",
+ accounts: map[string]hotline.Account{
+ "admin": {
+ Name: "admin",
+ Login: "admin",
+ Password: "$2a$04$2itGEYx8C1N5bsfRSoC9JuonS3I4YfnyVPZHLSwp7kEInRX0yoB.a",
+ Access: hotline.AccessBitmap{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00},
+ },
+ "Test User Name": {
+ Name: "test-user",
+ Login: "Test User Name",
+ Password: "$2a$04$9P/jgLn1fR9TjSoWL.rKxuN6g.1TSpf2o6Hw.aaRuBwrWIJNwsKkS",
+ Access: hotline.AccessBitmap{0x7d, 0xf0, 0x0c, 0xef, 0xab, 0x80, 0x00, 0x00},
+ },
+ "guest": {
+ Name: "guest",
+ Login: "guest",
+ Password: "$2a$04$6Yq/TIlgjSD.FbARwtYs9ODnkHawonu1TJ5W2jJKfhnHwBIQTk./y",
+ Access: hotline.AccessBitmap{0x7d, 0xf0, 0x0c, 0xef, 0xab, 0x80, 0x00, 0x00},
+ },
+ },
+ },
+ wantErr: assert.NoError,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := NewYAMLAccountManager(tt.args.accountDir)
+ if !tt.wantErr(t, err, fmt.Sprintf("NewYAMLAccountManager(%v)", tt.args.accountDir)) {
+ return
+ }
+
+ assert.Equal(t,
+ &hotline.Account{
+ Name: "admin",
+ Login: "admin",
+ Password: "$2a$04$2itGEYx8C1N5bsfRSoC9JuonS3I4YfnyVPZHLSwp7kEInRX0yoB.a",
+ Access: hotline.AccessBitmap{0xff, 0xff, 0xef, 0xff, 0xff, 0x80, 0x00, 0x00},
+ },
+ got.Get("admin"),
+ )
+
+ assert.Equal(t,
+ &hotline.Account{
+ Login: "guest",
+ Name: "guest",
+ Password: "$2a$04$6Yq/TIlgjSD.FbARwtYs9ODnkHawonu1TJ5W2jJKfhnHwBIQTk./y",
+ Access: hotline.AccessBitmap{0x60, 0x70, 0x0c, 0x20, 0x03, 0x80, 0x00, 0x00},
+ },
+ got.Get("guest"),
+ )
+
+ assert.Equal(t,
+ &hotline.Account{
+ Login: "test-user",
+ Name: "Test User Name",
+ Password: "$2a$04$9P/jgLn1fR9TjSoWL.rKxuN6g.1TSpf2o6Hw.aaRuBwrWIJNwsKkS",
+ Access: hotline.AccessBitmap{0x7d, 0xf0, 0x0c, 0xef, 0xab, 0x80, 0x00, 0x00},
+ },
+ got.Get("test-user"),
+ )
+ })
+ }
+}
diff --git a/internal/mobius/test/config/Users/admin.yaml b/internal/mobius/test/config/Users/admin.yaml
index 1bf656b..1c33eff 100644
--- a/internal/mobius/test/config/Users/admin.yaml
+++ b/internal/mobius/test/config/Users/admin.yaml
@@ -1,13 +1,45 @@
Login: admin
Name: admin
Password: $2a$04$2itGEYx8C1N5bsfRSoC9JuonS3I4YfnyVPZHLSwp7kEInRX0yoB.a
-Access:
-- 255
-- 255
-- 255
-- 255
-- 255
-- 255
-- 0
-- 0
-
+Access:
+ DownloadFile: true
+ DownloadFolder: true
+ UploadFile: true
+ UploadFolder: true
+ DeleteFile: true
+ RenameFile: true
+ MoveFile: true
+ CreateFolder: true
+ DeleteFolder: true
+ RenameFolder: true
+ MoveFolder: true
+ ReadChat: true
+ SendChat: true
+ OpenChat: true
+ CloseChat: true
+ ShowInList: true
+ CreateUser: true
+ DeleteUser: true
+ OpenUser: true
+ ModifyUser: true
+ ChangeOwnPass: true
+ NewsReadArt: true
+ NewsPostArt: true
+ DisconnectUser: true
+ CannotBeDisconnected: true
+ GetClientInfo: true
+ UploadAnywhere: true
+ AnyName: true
+ NoAgreement: true
+ SetFileComment: true
+ SetFolderComment: true
+ ViewDropBoxes: true
+ MakeAlias: true
+ Broadcast: true
+ NewsDeleteArt: true
+ NewsCreateCat: true
+ NewsDeleteCat: true
+ NewsCreateFldr: true
+ NewsDeleteFldr: true
+ SendPrivMsg: true
+FileRoot: ""
diff --git a/internal/mobius/test/config/Users/guest.yaml b/internal/mobius/test/config/Users/guest.yaml
index 57117bd..a0dd376 100644
--- a/internal/mobius/test/config/Users/guest.yaml
+++ b/internal/mobius/test/config/Users/guest.yaml
@@ -1,12 +1,45 @@
Login: guest
Name: guest
-Password: $2a$04$9P/jgLn1fR9TjSoWL.rKxuN6g.1TSpf2o6Hw.aaRuBwrWIJNwsKkS
+Password: $2a$04$6Yq/TIlgjSD.FbARwtYs9ODnkHawonu1TJ5W2jJKfhnHwBIQTk./y
Access:
-- 125
-- 240
-- 12
-- 239
-- 171
-- 128
-- 0
-- 0
+ DownloadFile: true
+ DownloadFolder: true
+ UploadFile: true
+ UploadFolder: true
+ DeleteFile: false
+ RenameFile: false
+ MoveFile: false
+ CreateFolder: false
+ DeleteFolder: false
+ RenameFolder: false
+ MoveFolder: false
+ ReadChat: true
+ SendChat: true
+ OpenChat: true
+ CloseChat: false
+ ShowInList: false
+ CreateUser: false
+ DeleteUser: false
+ OpenUser: false
+ ModifyUser: false
+ ChangeOwnPass: false
+ NewsReadArt: true
+ NewsPostArt: true
+ DisconnectUser: false
+ CannotBeDisconnected: false
+ GetClientInfo: false
+ UploadAnywhere: false
+ AnyName: true
+ NoAgreement: false
+ SetFileComment: false
+ SetFolderComment: false
+ ViewDropBoxes: false
+ MakeAlias: false
+ Broadcast: false
+ NewsDeleteArt: false
+ NewsCreateCat: false
+ NewsDeleteCat: false
+ NewsCreateFldr: false
+ NewsDeleteFldr: false
+ SendPrivMsg: true
+FileRoot: ""
diff --git a/internal/mobius/test/config/Users/user-with-old-access-format.yaml b/internal/mobius/test/config/Users/user-with-old-access-format.yaml
new file mode 100644
index 0000000..fa2f5cb
--- /dev/null
+++ b/internal/mobius/test/config/Users/user-with-old-access-format.yaml
@@ -0,0 +1,12 @@
+Login: test-user
+Name: Test User Name
+Password: $2a$04$9P/jgLn1fR9TjSoWL.rKxuN6g.1TSpf2o6Hw.aaRuBwrWIJNwsKkS
+Access:
+ - 125
+ - 240
+ - 12
+ - 239
+ - 171
+ - 128
+ - 0
+ - 0
diff --git a/internal/mobius/transaction_handlers.go b/internal/mobius/transaction_handlers.go
index 6f734c7..d18dba5 100644
--- a/internal/mobius/transaction_handlers.go
+++ b/internal/mobius/transaction_handlers.go
@@ -1329,7 +1329,7 @@ func HandleDownloadFile(cc *hotline.ClientConn, t *hotline.Transaction) (res []h
// Download all files from the specified folder and sub-folders
func HandleDownloadFolder(cc *hotline.ClientConn, t *hotline.Transaction) (res []hotline.Transaction) {
- if !cc.Authorize(hotline.AccessDownloadFile) {
+ if !cc.Authorize(hotline.AccessDownloadFolder) {
return cc.NewErrReply(t, "You are not allowed to download folders.")
}
@@ -1372,6 +1372,10 @@ func HandleDownloadFolder(cc *hotline.ClientConn, t *hotline.Transaction) (res [
// 220 Folder item count
// 204 File transfer options "Optional Currently set to 1" (TODO: ??)
func HandleUploadFolder(cc *hotline.ClientConn, t *hotline.Transaction) (res []hotline.Transaction) {
+ if !cc.Authorize(hotline.AccessUploadFolder) {
+ return cc.NewErrReply(t, "You are not allowed to upload folders.")
+ }
+
var fp hotline.FilePath
if t.GetField(hotline.FieldFilePath).Data != nil {
if _, err := fp.Write(t.GetField(hotline.FieldFilePath).Data); err != nil {
diff --git a/internal/mobius/transaction_handlers_test.go b/internal/mobius/transaction_handlers_test.go
index 26ea950..43c033f 100644
--- a/internal/mobius/transaction_handlers_test.go
+++ b/internal/mobius/transaction_handlers_test.go
@@ -3774,3 +3774,97 @@ func TestHandlePostNewsArt(t *testing.T) {
})
}
}
+
+func TestHandleUploadFolder(t *testing.T) {
+ type args struct {
+ cc *hotline.ClientConn
+ t hotline.Transaction
+ }
+ tests := []struct {
+ name string
+ args args
+ wantRes []hotline.Transaction
+ }{
+ {
+ name: "when user does not have required access",
+ args: args{
+ cc: &hotline.ClientConn{
+ Account: &hotline.Account{
+ Access: hotline.AccessBitmap{},
+ },
+ },
+ t: hotline.NewTransaction(
+ hotline.TranUploadFldr, [2]byte{0, 1},
+ hotline.NewField(hotline.FieldFileName, []byte("testFile")),
+ hotline.NewField(hotline.FieldFilePath, []byte{
+ 0x00, 0x01,
+ 0x00, 0x00,
+ 0x03,
+ 0x2e, 0x2e, 0x2f,
+ }),
+ ),
+ },
+ wantRes: []hotline.Transaction{
+ {
+ IsReply: 0x01,
+ ErrorCode: [4]byte{0, 0, 0, 1},
+ Fields: []hotline.Field{
+ hotline.NewField(hotline.FieldError, []byte("You are not allowed to upload folders.")),
+ },
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ TranAssertEqual(t, tt.wantRes, HandleUploadFolder(tt.args.cc, &tt.args.t))
+ })
+ }
+}
+
+func TestHandleDownloadFolder(t *testing.T) {
+ type args struct {
+ cc *hotline.ClientConn
+ t hotline.Transaction
+ }
+ tests := []struct {
+ name string
+ args args
+ wantRes []hotline.Transaction
+ }{
+ {
+ name: "when user does not have required access",
+ args: args{
+ cc: &hotline.ClientConn{
+ Account: &hotline.Account{
+ Access: hotline.AccessBitmap{},
+ },
+ },
+ t: hotline.NewTransaction(
+ hotline.TranDownloadFldr, [2]byte{0, 1},
+ hotline.NewField(hotline.FieldFileName, []byte("testFile")),
+ hotline.NewField(hotline.FieldFilePath, []byte{
+ 0x00, 0x01,
+ 0x00, 0x00,
+ 0x03,
+ 0x2e, 0x2e, 0x2f,
+ }),
+ ),
+ },
+ wantRes: []hotline.Transaction{
+ {
+ IsReply: 0x01,
+ ErrorCode: [4]byte{0, 0, 0, 1},
+ Fields: []hotline.Field{
+ hotline.NewField(hotline.FieldError, []byte("You are not allowed to download folders.")),
+ },
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ TranAssertEqual(t, tt.wantRes, HandleDownloadFolder(tt.args.cc, &tt.args.t))
+ })
+ }
+}