From: Jeff Halter Date: Sun, 28 Jul 2024 19:43:23 +0000 (-0700) Subject: Improve human readability of account config files X-Git-Url: https://git.r.bdr.sh/rbdr/mobius/commitdiff_plain/72f8a1fd5e7fbd5224e3f1393f36cc9b0d58eb78?hp=09261d2b52fa739bb6321c866566223f11061201 Improve human readability of account config files --- 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<= 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)) + }) + } +}