From 5f68d84ca585aed72937c067d83f3b7526a35647 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Fri, 2 Dec 2016 15:42:10 +0200 Subject: [PATCH] Removed register GET request in favor of POST, and did required HTTP api changes --- acmetxt.go | 16 ++++++++++++++++ api.go | 31 +++++++++++++++--------------- api_test.go | 54 +++++++++++++++++++++++++++++++++++++++++++---------- main.go | 1 - 4 files changed, 75 insertions(+), 27 deletions(-) diff --git a/acmetxt.go b/acmetxt.go index a4a23ed..e65c842 100644 --- a/acmetxt.go +++ b/acmetxt.go @@ -41,6 +41,22 @@ func (c *cidrslice) ValidEntries() []string { return valid } +// Check if IP belongs to an allowed net +func (a ACMETxt) allowedFrom(ip string) bool { + remoteIP := net.ParseIP(ip) + // Range not limited + if len(a.AllowFrom.ValidEntries()) == 0 { + return true + } + for _, v := range a.AllowFrom.ValidEntries() { + _, vnet, _ := net.ParseCIDR(v) + if vnet.Contains(remoteIP) { + return true + } + } + return false +} + func newACMETxt() ACMETxt { var a = ACMETxt{} password := generatePassword(40) diff --git a/api.go b/api.go index 5e78321..dfb9e2d 100644 --- a/api.go +++ b/api.go @@ -23,16 +23,18 @@ func (a authMiddleware) Serve(ctx *iris.Context) { } else { if correctPassword(password, au.Password) { // Password ok - if err := ctx.ReadJSON(&postData); err == nil { - // Check that the subdomain belongs to the user - if au.Subdomain == postData.Subdomain { - ctx.Next() + if au.allowedFrom(ctx.RequestIP()) { + // Update is allowed from remote addr + if err := ctx.ReadJSON(&postData); err == nil { + if au.Subdomain == postData.Subdomain { + ctx.Next() + return + } + } else { + // JSON error + ctx.JSON(iris.StatusBadRequest, iris.Map{"error": "bad data"}) return } - } else { - // JSON error - ctx.JSON(iris.StatusBadRequest, iris.Map{"error": "bad data"}) - return } } else { // Wrong password @@ -44,17 +46,19 @@ func (a authMiddleware) Serve(ctx *iris.Context) { } func webRegisterPost(ctx *iris.Context) { - // Create new user - nu, err := DB.Register(cidrslice{}) var regJSON iris.Map var regStatus int + cslice := cidrslice{} + _ = ctx.ReadJSON(&cslice) + // Create new user + nu, err := DB.Register(cslice) if err != nil { errstr := fmt.Sprintf("%v", err) regJSON = iris.Map{"error": errstr} regStatus = iris.StatusInternalServerError log.WithFields(log.Fields{"error": err.Error()}).Debug("Error in registration") } else { - regJSON = iris.Map{"username": nu.Username, "password": nu.Password, "fulldomain": nu.Subdomain + "." + DNSConf.General.Domain, "subdomain": nu.Subdomain} + regJSON = iris.Map{"username": nu.Username, "password": nu.Password, "fulldomain": nu.Subdomain + "." + DNSConf.General.Domain, "subdomain": nu.Subdomain, "allowfrom": nu.AllowFrom.JSON()} regStatus = iris.StatusCreated log.WithFields(log.Fields{"user": nu.Username.String()}).Debug("Created new user") @@ -62,11 +66,6 @@ func webRegisterPost(ctx *iris.Context) { ctx.JSON(regStatus, regJSON) } -func webRegisterGet(ctx *iris.Context) { - // This is placeholder for now - webRegisterPost(ctx) -} - func webUpdatePost(ctx *iris.Context) { // User auth done in middleware a := ACMETxt{} diff --git a/api_test.go b/api_test.go index f69f6bf..6a054ee 100644 --- a/api_test.go +++ b/api_test.go @@ -26,7 +26,6 @@ func setupIris(t *testing.T, debug bool, noauth bool) *httpexpect.Expect { } DNSConf = dnscfg var ForceAuth = authMiddleware{} - iris.Get("/register", webRegisterGet) iris.Post("/register", webRegisterPost) if noauth { iris.Post("/update", webUpdatePost) @@ -40,14 +39,6 @@ func setupIris(t *testing.T, debug bool, noauth bool) *httpexpect.Expect { func TestApiRegister(t *testing.T) { e := setupIris(t, false, false) - e.GET("/register").Expect(). - Status(iris.StatusCreated). - JSON().Object(). - ContainsKey("fulldomain"). - ContainsKey("subdomain"). - ContainsKey("username"). - ContainsKey("password"). - NotContainsKey("error") e.POST("/register").Expect(). Status(iris.StatusCreated). JSON().Object(). @@ -56,6 +47,27 @@ func TestApiRegister(t *testing.T) { ContainsKey("username"). ContainsKey("password"). NotContainsKey("error") + + allowfrom := []interface{}{ + "123.123.123.123/32", + "1010.10.10.10/24", + "invalid", + } + + response := e.POST("/register"). + WithJSON(allowfrom). + Expect(). + Status(iris.StatusCreated). + JSON().Object(). + ContainsKey("fulldomain"). + ContainsKey("subdomain"). + ContainsKey("username"). + ContainsKey("password"). + ContainsKey("allowfrom"). + NotContainsKey("error") + + response.Value("allowfrom").String().Equal("[\"123.123.123.123/32\"]") + } func TestApiRegisterWithMockDB(t *testing.T) { @@ -66,7 +78,7 @@ func TestApiRegisterWithMockDB(t *testing.T) { defer db.Close() mock.ExpectBegin() mock.ExpectPrepare("INSERT INTO records").WillReturnError(errors.New("error")) - e.GET("/register").Expect(). + e.POST("/register").Expect(). Status(iris.StatusInternalServerError). JSON().Object(). ContainsKey("error") @@ -146,10 +158,30 @@ func TestApiManyUpdateWithCredentials(t *testing.T) { "txt": ""} e := setupIris(t, false, false) + // User without defined CIDR masks newUser, err := DB.Register(cidrslice{}) if err != nil { t.Errorf("Could not create new user, got error [%v]", err) } + + // User with defined allow from - CIDR masks, all invalid + // (httpexpect doesn't provide a way to mock remote ip) + newUserWithCIDR, err := DB.Register(cidrslice{"192.168.1.1/32", "invalid"}) + if err != nil { + t.Errorf("Could not create new user with CIDR, got error [%v]", err) + } + + // Another user with valid CIDR mask to match the httpexpect default + newUserWithValidCIDR, err := DB.Register(cidrslice{"0.0.0.0/32", "invalid"}) + if err != nil { + t.Errorf("Could not create new user with a valid CIDR, got error [%v]", err) + } + + /* newUserWithValidCIDR, err := DB.Register(cidrslice{"192.168.1.1/32", "invalid"}) + if err != nil { + t.Errorf("Could not create new user with CIDR, got error [%v]", err) + } + */ for _, test := range []struct { user string pass string @@ -164,6 +196,8 @@ func TestApiManyUpdateWithCredentials(t *testing.T) { {newUser.Username.String(), newUser.Password, newUser.Subdomain, "tooshortfortxt", 400}, {newUser.Username.String(), newUser.Password, newUser.Subdomain, 1234567890, 400}, {newUser.Username.String(), newUser.Password, newUser.Subdomain, validTxtData, 200}, + {newUserWithCIDR.Username.String(), newUserWithCIDR.Password, newUserWithCIDR.Subdomain, validTxtData, 401}, + {newUserWithValidCIDR.Username.String(), newUserWithValidCIDR.Password, newUserWithValidCIDR.Subdomain, validTxtData, 200}, {newUser.Username.String(), "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", newUser.Subdomain, validTxtData, 401}, } { updateJSON = map[string]interface{}{ diff --git a/main.go b/main.go index d114ad3..2ba08cd 100644 --- a/main.go +++ b/main.go @@ -49,7 +49,6 @@ func startHTTPAPI() { }) api.Use(crs) var ForceAuth = authMiddleware{} - api.Get("/register", webRegisterGet) api.Post("/register", webRegisterPost) api.Post("/update", ForceAuth.Serve, webUpdatePost) switch DNSConf.API.TLS {