package main import ( "log" "net" "net/http" "reflect" "testing" "github.com/google/go-cmp/cmp" ) var ( addr = "localhost" port uint16 = 1234 rawCfg = Config{ ServerPort: port, Users: []User{ User{ Username: "user", // "secret" -> bcrypt-hashed Password: "$2a$12$He9X4KNAFJQy0CL2c9.Df.tjXwCkideOogwJ7DNtO/I8qzeJZfc3i", Records: []string{ "record.example.org", }, }, User{ Username: "user2", // "secret" -> bcrypt-hashed Password: "$2a$12$He9X4KNAFJQy0CL2c9.Df.tjXwCkideOogwJ7DNtO/I8qzeJZfc3i", Records: []string{ "record2.example.org", }, }, User{ Username: "bad-hash", Password: "secret", Records: []string{ "record3.example.org", }, }, }, RRConfigs: []RRConfig{ RRConfig{ Recordname: "record.example.org", Zonename: "zone.example.org", Nameserver: "ns1.example.org", Tsigalgo: "hmac-sha256", Tsigid: "tsig-id", Tsigkey: "some-secret-key", }, RRConfig{ Recordname: "record2.example.org", Zonename: "zone.example.org", Nameserver: "ns1.example.org", Tsigalgo: "hmac-sha256", Tsigid: "tsig-id", Tsigkey: "some-secret-key", }, RRConfig{ Recordname: "record3.example.org", Zonename: "zone.example.org", Nameserver: "ns1.example.org", Tsigalgo: "hmac-sha256", Tsigid: "tsig-id", Tsigkey: "some-secret-key", }, }, } cfg *Config ) // pre-initialize config for all testcases func init() { var err error cfg, err = prepareConfig(&rawCfg) if err != nil { log.Fatalf("Preparing config failed: %v", err) } } func prepareRequest(t *testing.T, params map[string]string, headers map[string]string) *http.Request { req, err := http.NewRequest("GET", "http://"+addr, nil) if err != nil { t.Fatalf("Generating request failed: %v", err) } // append http parameters to http request q := req.URL.Query() for k, v := range params { q.Add(k, v) } req.URL.RawQuery = q.Encode() // set headers for k, v := range headers { req.Header.Set(k, v) } return req } type credentials struct { username string password string } func TestIsAuthenticated(t *testing.T) { tests := []struct { name string auth *credentials expUser *User }{ { "missing basicauth", nil, nil, }, { "non-existent user", &credentials{ "unknown", "secret", }, nil, }, { "invalid hash", &credentials{ "bad-hash", "secret", }, nil, }, { "correct user", &credentials{ "user", "secret", }, &User{ Username: "user", Password: "$2a$12$He9X4KNAFJQy0CL2c9.Df.tjXwCkideOogwJ7DNtO/I8qzeJZfc3i", Records: []string{ "record.example.org", }, records: map[string]bool{ "record.example.org": true, }, }, }, } for _, tc := range tests { req := prepareRequest(t, nil, nil) if tc.auth != nil { req.SetBasicAuth(tc.auth.username, tc.auth.password) } res := isAuthenticated(cfg, req) if !reflect.DeepEqual(tc.expUser, res) { t.Errorf("%s: res: %s", tc.name, cmp.Diff(tc.expUser, res, cmp.AllowUnexported(User{}))) } } } func TestVerifyHostname(t *testing.T) { tests := []struct { name string user *User hostname string expMsg string expRrconfig *RRConfig }{ { "empty hostname", cfg.users["user"], "", "nohost", nil, }, { "user not allowed", cfg.users["user2"], "record.example.org", "badauth", nil, }, { "correct hostname", cfg.users["user"], "record.example.org", "", cfg.rrconfigs["record.example.org"], }, } for _, tc := range tests { msg, rrconfig := verifyHostname(cfg, tc.user, tc.hostname) if !reflect.DeepEqual(tc.expMsg, msg) { t.Errorf("%s: res: %s", tc.name, cmp.Diff(tc.expMsg, msg)) } if !reflect.DeepEqual(tc.expRrconfig, rrconfig) { t.Errorf("%s: res: %s", tc.name, cmp.Diff(tc.expRrconfig, rrconfig)) } } } func TestGetIpAddress(t *testing.T) { tests := []struct { name string params map[string]string headers map[string]string remoteAddr string expAddr net.IP }{ { "no address", nil, nil, "", nil, }, { "remote addr - invalid ip", nil, nil, "1.1.1:1234", nil, }, { "remote addr - missing port", nil, nil, "1.1.1.1", nil, }, { "remote addr - valid", nil, nil, "1.1.1.1:1234", net.ParseIP("1.1.1.1"), }, { "X-Real-IP - empty", nil, map[string]string{ "X-Real-IP": "", }, "1.1.1.1:1234", net.ParseIP("1.1.1.1"), }, { "X-Real-IP - invalid", nil, map[string]string{ "X-Real-IP": "2.2.2", }, "1.1.1.1:1234", nil, }, { "X-Real-IP - valid", nil, map[string]string{ "X-Real-IP": "2.2.2.2", }, "1.1.1.1:1234", net.ParseIP("2.2.2.2"), }, { "X-Forwarded-For - empty", nil, map[string]string{ "X-Forwarded-For": "", "X-Real-IP": "2.2.2.2", }, "1.1.1.1:1234", net.ParseIP("2.2.2.2"), }, { "X-Forwarded-For - comma only", nil, map[string]string{ "X-Forwarded-For": ",", "X-Real-IP": "2.2.2.2", }, "1.1.1.1:1234", nil, }, { "X-Forwarded-For - invalid entry", nil, map[string]string{ "X-Forwarded-For": "3.3.3", "X-Real-IP": "2.2.2.2", }, "1.1.1.1:1234", nil, }, { "X-Forwarded-For - one entry", nil, map[string]string{ "X-Forwarded-For": "3.3.3.3", "X-Real-IP": "2.2.2.2", }, "1.1.1.1:1234", net.ParseIP("3.3.3.3"), }, { "X-Forwarded-For - multiple entries", nil, map[string]string{ "X-Forwarded-For": "3.3.3.3, 3.3.3.4", "X-Real-IP": "2.2.2.2", }, "1.1.1.1:1234", net.ParseIP("3.3.3.3"), }, { "myip - empty", map[string]string{ "myip": "", }, map[string]string{ "X-Forwarded-For": "3.3.3.3, 3.3.3.4", "X-Real-IP": "2.2.2.2", }, "1.1.1.1:1234", net.ParseIP("3.3.3.3"), }, { "myip - invalid", map[string]string{ "myip": "1.2.3", }, map[string]string{ "X-Forwarded-For": "3.3.3.3, 3.3.3.4", "X-Real-IP": "2.2.2.2", }, "1.1.1.1:1234", nil, }, { "myip - valid", map[string]string{ "myip": "4.4.4.4", }, map[string]string{ "X-Forwarded-For": "3.3.3.3, 3.3.3.4", "X-Real-IP": "2.2.2.2", }, "1.1.1.1:1234", net.ParseIP("4.4.4.4"), }, } for _, tc := range tests { req := prepareRequest(t, tc.params, tc.headers) req.RemoteAddr = tc.remoteAddr addr := getIpAddress(req) if !reflect.DeepEqual(tc.expAddr, addr) { t.Errorf("%s: res: %s", tc.name, cmp.Diff(tc.expAddr, addr)) } } }