]> git.r.bdr.sh - rbdr/mobius/blame - hotline/server_blackbox_test.go
Sanitize file path input to prevent directory traversal
[rbdr/mobius] / hotline / server_blackbox_test.go
CommitLineData
6988a057
JH
1package hotline
2
625b0580
JH
3import (
4 "bytes"
5 "context"
6 "fmt"
00d1ef67
JH
7 "github.com/davecgh/go-spew/spew"
8 "github.com/stretchr/testify/assert"
625b0580
JH
9 "go.uber.org/zap"
10 "go.uber.org/zap/zapcore"
11 "net"
12 "os"
625b0580
JH
13 "testing"
14)
15
16type testCase struct {
17 name string // test case description
18 account Account // Account struct for a user that will test transaction will execute under
19 request *Transaction // transaction that will be sent by the client to the server
20 setup func() // Optional test-specific setup required for the scenario
21 teardown func() // Optional test-specific teardown for the scenario
22 mockHandler map[int]*mockClientHandler
23}
24
25func (tt *testCase) Setup(srv *Server) error {
b25c4a19 26 if err := srv.NewUser(tt.account.Login, tt.account.Name, string(negateString([]byte(tt.account.Password))), *tt.account.Access); err != nil {
625b0580
JH
27 return err
28 }
29
30 if tt.setup != nil {
31 tt.setup()
32 }
33
34 return nil
35}
36
37func (tt *testCase) Teardown(srv *Server) error {
38 if err := srv.DeleteUser(tt.account.Login); err != nil {
39 return err
40 }
41
42 if tt.teardown != nil {
43 tt.teardown()
44 }
45
46 return nil
47}
48
49func NewTestLogger() *zap.SugaredLogger {
50 encoderCfg := zap.NewProductionEncoderConfig()
51 encoderCfg.TimeKey = "timestamp"
52 encoderCfg.EncodeTime = zapcore.ISO8601TimeEncoder
53
54 core := zapcore.NewCore(
55 zapcore.NewConsoleEncoder(encoderCfg),
56 zapcore.Lock(os.Stdout),
57 zap.DebugLevel,
58 )
59
60 cores := []zapcore.Core{core}
61 l := zap.New(zapcore.NewTee(cores...))
62 defer func() { _ = l.Sync() }()
63 return l.Sugar()
64}
65
66func StartTestServer() (*Server, context.Context, context.CancelFunc) {
67 ctx, cancelRoot := context.WithCancel(context.Background())
68
92a7e455
JH
69 FS = OSFileStore{}
70
625b0580
JH
71 srv, err := NewServer("test/config/", "localhost", 0, NewTestLogger())
72 if err != nil {
73 panic(err)
74 }
75
76 go func() {
77 err := srv.ListenAndServe(ctx, cancelRoot)
78 if err != nil {
79 panic(err)
80 }
81 }()
82
83 return srv, ctx, cancelRoot
84}
85
86func TestHandshake(t *testing.T) {
92a7e455
JH
87 mfs := MockFileStore{}
88 fh, _ := os.Open("./test/config/Agreement.txt")
89 mfs.On("Open", "/test/config/Agreement.txt").Return(fh, nil)
90 fh, _ = os.Open("./test/config/config.yaml")
91 mfs.On("Open", "/test/config/config.yaml").Return(fh, nil)
92 FS = mfs
93 spew.Dump(mfs)
94
625b0580
JH
95 srv, _, cancelFunc := StartTestServer()
96 defer cancelFunc()
97
98 port := srv.APIListener.Addr().(*net.TCPAddr).Port
99
100 conn, err := net.Dial("tcp", fmt.Sprintf(":%v", port))
101 if err != nil {
102 t.Fatal(err)
103 }
104 defer conn.Close()
105
106 conn.Write([]byte{0x54, 0x52, 0x54, 0x50, 0x00, 0x01, 0x00, 0x00})
107
108 replyBuf := make([]byte, 8)
109 _, _ = conn.Read(replyBuf)
110
111 want := []byte{84, 82, 84, 80, 0, 0, 0, 0}
112 if bytes.Compare(replyBuf, want) != 0 {
113 t.Errorf("%q, want %q", replyBuf, want)
114 }
115
116}
117
118//func TestLogin(t *testing.T) {
6988a057 119//
625b0580
JH
120// tests := []struct {
121// name string
122// client *Client
123// }{
6988a057 124// {
625b0580
JH
125// name: "when login is successful",
126// client: NewClient("guest", NewTestLogger()),
6988a057 127// },
6988a057 128// }
6988a057
JH
129// for _, test := range tests {
130// t.Run(test.name, func(t *testing.T) {
6988a057 131//
6988a057
JH
132// })
133// }
134//}
625b0580 135
625b0580
JH
136func TestNewUser(t *testing.T) {
137 srv, _, _ := StartTestServer()
138
139 tests := []testCase{
140 //{
141 // name: "a valid new account",
142 // mockHandler: func() mockClientHandler {
143 // mh := mockClientHandler{}
144 // mh.On("Handle", mock.AnythingOfType("*hotline.Client"), mock.MatchedBy(func(t *Transaction) bool {
145 // println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
146 // spew.Dump(t.Type)
147 // spew.Dump(bytes.Equal(t.Type, []byte{0x01, 0x5e}))
148 // //if !bytes.Equal(t.GetField(fieldError).Data, []byte("You are not allowed to create new accounts.")) {
149 // // return false
150 // //}
151 // return bytes.Equal(t.Type, []byte{0x01, 0x5e},
152 // )
153 // })).Return(
154 // []Transaction{}, nil,
155 // )
156 //
157 // clientHandlers[tranNewUser] = mh
158 // return mh
159 // }(),
160 // client: func() *Client {
161 // c := NewClient("testUser", NewTestLogger())
162 // return c
163 // }(),
164 // teardown: func() {
165 // _ = srv.DeleteUser("testUser")
166 // },
167 // account: Account{
168 // Login: "test",
169 // Name: "unnamed",
170 // Password: "test",
171 // Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
172 // },
173 // request: NewTransaction(
174 // tranNewUser, nil,
175 // NewField(fieldUserLogin, []byte(NegatedUserString([]byte("testUser")))),
176 // NewField(fieldUserName, []byte("testUserName")),
177 // NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))),
178 // NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
179 // ),
180 // want: &Transaction{
181 // Fields: []Field{},
182 // },
183 //},
184 //{
185 // name: "a newUser request from a user without the required access",
186 // mockHandler: func() *mockClientHandler {
187 // mh := mockClientHandler{}
188 // mh.On("Handle", mock.AnythingOfType("*hotline.Client"), mock.MatchedBy(func(t *Transaction) bool {
189 // if !bytes.Equal(t.GetField(fieldError).Data, []byte("You are not allowed to create new accounts.")) {
190 // return false
191 // }
192 // return bytes.Equal(t.Type, []byte{0x01, 0x5e})
193 // })).Return(
194 // []Transaction{}, nil,
195 // )
196 // return &mh
197 // }(),
198 // teardown: func() {
199 // _ = srv.DeleteUser("testUser")
200 // },
201 // account: Account{
202 // Login: "test",
203 // Name: "unnamed",
204 // Password: "test",
205 // Access: &[]byte{0, 0, 0, 0, 0, 0, 0, 0},
206 // },
207 // request: NewTransaction(
208 // tranNewUser, nil,
209 // NewField(fieldUserLogin, []byte(NegatedUserString([]byte("testUser")))),
210 // NewField(fieldUserName, []byte("testUserName")),
211 // NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))),
212 // NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
213 // ),
214 //},
215 //{
216 // name: "when user does not have required permission",
217 // mockHandler: func() map[int]*mockClientHandler {
218 // mockHandlers := make(map[int]*mockClientHandler)
219 //
220 // mh := mockClientHandler{}
221 // mh.On("Handle", mock.AnythingOfType("*hotline.Client"), mock.MatchedBy(func(t *Transaction) bool {
222 // return t.equal(Transaction{
223 // Type: []byte{0x01, 0x5e},
224 // IsReply: 1,
225 // ErrorCode: []byte{0, 0, 0, 1},
226 // Fields: []Field{
227 // NewField(fieldError, []byte("You are not allowed to create new accounts.")),
228 // },
229 // })
230 // })).Return(
231 // []Transaction{}, nil,
232 // )
233 // mockHandlers[tranNewUser] = &mh
234 //
235 // return mockHandlers
236 // }(),
237 //
238 // teardown: func() {
239 // _ = srv.DeleteUser("testUser")
240 // },
241 // account: Account{
242 // Login: "test",
243 // Name: "unnamed",
244 // Password: "test",
245 // Access: &[]byte{0, 0, 0, 0, 0, 0, 0, 0},
246 // },
247 // request: NewTransaction(
248 // tranNewUser, nil,
249 // NewField(fieldUserLogin, []byte(NegatedUserString([]byte("testUser")))),
250 // NewField(fieldUserName, []byte("testUserName")),
251 // NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))),
252 // NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
253 // ),
254 //},
255
256 //{
257 // name: "a request to create a user that already exists",
258 // setup: func() {
259 //
260 // },
261 // teardown: func() {
262 // _ = srv.DeleteUser("testUser")
263 // },
264 // account: Account{
265 // Login: "test",
266 // Name: "unnamed",
267 // Password: "test",
268 // Access: &[]byte{255, 255, 255, 255, 255, 255, 255, 255},
269 // },
270 // request: NewTransaction(
271 // tranNewUser, nil,
272 // NewField(fieldUserLogin, []byte(NegatedUserString([]byte("guest")))),
273 // NewField(fieldUserName, []byte("testUserName")),
274 // NewField(fieldUserPassword, []byte(NegatedUserString([]byte("testPw")))),
275 // NewField(fieldUserAccess, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}),
276 // ),
277 // want: &Transaction{
278 // Fields: []Field{
279 // NewField(fieldError, []byte("Cannot create account guest because there is already an account with that login.")),
280 // },
281 // },
282 //},
283 }
284
285 for _, test := range tests {
286 t.Run(test.name, func(t *testing.T) {
287 test.Setup(srv)
288
289 // move to Setup?
290 c := NewClient(test.account.Name, NewTestLogger())
291 err := c.JoinServer(fmt.Sprintf(":%v", srv.APIPort()), test.account.Login, test.account.Password)
292 if err != nil {
293 t.Errorf("login failed: %v", err)
294 }
295 // end move to Setup??
296
297 for key, value := range test.mockHandler {
298 c.Handlers[uint16(key)] = value
299 }
300
301 // send test case request
302 _ = c.Send(*test.request)
303
304 //time.Sleep(1 * time.Second)
305 // ===
306
307 transactions, _ := readN(c.Connection, 1)
308 for _, t := range transactions {
309 _ = c.HandleTransaction(&t)
310 }
311
312 // ===
313
314 for _, handler := range test.mockHandler {
315 handler.AssertExpectations(t)
316 }
317
318 test.Teardown(srv)
319 })
320 }
321}
322
00d1ef67
JH
323func tranAssertEqual(t *testing.T, tran1, tran2 []Transaction) bool {
324 var newT1 []Transaction
325 var newT2 []Transaction
92a7e455
JH
326 for _, trans := range tran1 {
327 trans.ID = []byte{0, 0, 0, 0}
00d1ef67
JH
328 newT1 = append(newT1, trans)
329 }
625b0580 330
92a7e455
JH
331 for _, trans := range tran2 {
332 trans.ID = []byte{0, 0, 0, 0}
00d1ef67 333 newT2 = append(newT2, trans)
625b0580 334
00d1ef67 335 }
625b0580 336
00d1ef67 337 return assert.Equal(t, newT1, newT2)
92a7e455 338}