Skip to content

Commit 0dfe549

Browse files
committed
feat(whitelist): Add support for IP whitelisting
1 parent ead3aa2 commit 0dfe549

4 files changed

Lines changed: 251 additions & 3 deletions

File tree

api/appsettings.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,17 @@ type AppSettings struct {
1616
// Maintenance determines if the application is taken down for maintenance or not.
1717
Maintenance *bool `json:"maintenance,omitempty"`
1818
// Routable determines if the application should be exposed by the router.
19-
Routable *bool `json:"routable,omitempty"`
19+
Routable *bool `json:"routable,omitempty"`
20+
Whitelist []string `json:"whitelist,omitempty"`
2021
}
2122

2223
// NewRoutable returns a default value for the AppSettings.Routable field.
2324
func NewRoutable() *bool {
2425
b := true
2526
return &b
2627
}
28+
29+
// Whitelist is the structure of POST /v2/app/<app id>/whitelist/.
30+
type Whitelist struct {
31+
Addresses []string `json:"addresses"`
32+
}

appsettings/appsettings_test.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const appSettingsFixture string = `
1818
"app": "example-go",
1919
"maintenance": true,
2020
"routable": true,
21+
"whitelist": ["1.2.3.4", "0.0.0.0/0"],
2122
"created": "2014-01-01T00:00:00UTC",
2223
"updated": "2014-01-01T00:00:00UTC",
2324
"uuid": "de1bf5b5-4a72-4f94-a10c-d2a3741cdf75"
@@ -30,14 +31,15 @@ const appSettingsUnsetFixture string = `
3031
"app": "unset-test",
3132
"maintenance": true,
3233
"routable": true,
34+
"whitelist": ["1.2.3.4", "0.0.0.0/0"],
3335
"created": "2014-01-01T00:00:00UTC",
3436
"updated": "2014-01-01T00:00:00UTC",
3537
"uuid": "de1bf5b5-4a72-4f94-a10c-d2a3741cdf75"
3638
}
3739
`
3840

39-
const appSettingsSetExpected string = `{"maintenance":true,"routable":true}`
40-
const appSettingsUnsetExpected string = `{"maintenance":true,"routable":true}`
41+
const appSettingsSetExpected string = `{"maintenance":true,"routable":true,"whitelist":["1.2.3.4","0.0.0.0/0"]}`
42+
const appSettingsUnsetExpected string = `{"maintenance":true,"routable":true,"whitelist":["1.2.3.4","0.0.0.0/0"]}`
4143

4244
var trueVar = true
4345

@@ -126,6 +128,7 @@ func TestAppSettingsSet(t *testing.T) {
126128
App: "example-go",
127129
Routable: api.NewRoutable(),
128130
Maintenance: &trueVar,
131+
Whitelist: []string{"1.2.3.4", "0.0.0.0/0"},
129132
Created: "2014-01-01T00:00:00UTC",
130133
Updated: "2014-01-01T00:00:00UTC",
131134
UUID: "de1bf5b5-4a72-4f94-a10c-d2a3741cdf75",
@@ -134,6 +137,7 @@ func TestAppSettingsSet(t *testing.T) {
134137
appSettingsVars := api.AppSettings{
135138
Maintenance: &trueVar,
136139
Routable: api.NewRoutable(),
140+
Whitelist: []string{"1.2.3.4", "0.0.0.0/0"},
137141
}
138142

139143
actual, err := Set(deis, "example-go", appSettingsVars)
@@ -164,6 +168,7 @@ func TestAppSettingsUnset(t *testing.T) {
164168
App: "unset-test",
165169
Maintenance: &trueVar,
166170
Routable: api.NewRoutable(),
171+
Whitelist: []string{"1.2.3.4", "0.0.0.0/0"},
167172
Created: "2014-01-01T00:00:00UTC",
168173
Updated: "2014-01-01T00:00:00UTC",
169174
UUID: "de1bf5b5-4a72-4f94-a10c-d2a3741cdf75",
@@ -172,6 +177,7 @@ func TestAppSettingsUnset(t *testing.T) {
172177
appSettingsVars := api.AppSettings{
173178
Maintenance: &trueVar,
174179
Routable: api.NewRoutable(),
180+
Whitelist: []string{"1.2.3.4", "0.0.0.0/0"},
175181
}
176182

177183
actual, err := Set(deis, "unset-test", appSettingsVars)
@@ -202,6 +208,7 @@ func TestAppSettingsList(t *testing.T) {
202208
App: "example-go",
203209
Maintenance: &trueVar,
204210
Routable: api.NewRoutable(),
211+
Whitelist: []string{"1.2.3.4", "0.0.0.0/0"},
205212
Created: "2014-01-01T00:00:00UTC",
206213
Updated: "2014-01-01T00:00:00UTC",
207214
UUID: "de1bf5b5-4a72-4f94-a10c-d2a3741cdf75",

whitelist/whitelist.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Package whitelist provides methods for managing an app's whitelisted IP's.
2+
package whitelist
3+
4+
import (
5+
"encoding/json"
6+
"fmt"
7+
8+
deis "github.com/deis/controller-sdk-go"
9+
"github.com/deis/controller-sdk-go/api"
10+
)
11+
12+
// List IP's whitelisted for an app.
13+
func List(c *deis.Client, appID string) (api.Whitelist, error) {
14+
u := fmt.Sprintf("/v2/apps/%s/whitelist/", appID)
15+
res, reqErr := c.Request("GET", u, nil)
16+
if reqErr != nil && !deis.IsErrAPIMismatch(reqErr) {
17+
return api.Whitelist{}, reqErr
18+
}
19+
defer res.Body.Close()
20+
21+
whitelist := api.Whitelist{}
22+
if err := json.NewDecoder(res.Body).Decode(&whitelist); err != nil {
23+
return api.Whitelist{}, err
24+
}
25+
26+
return whitelist, reqErr
27+
}
28+
29+
// Add adds addresses to an app's whitelist.
30+
func Add(c *deis.Client, appID string, addresses []string) (api.Whitelist, error) {
31+
u := fmt.Sprintf("/v2/apps/%s/whitelist/", appID)
32+
33+
req := api.Whitelist{Addresses: addresses}
34+
body, err := json.Marshal(req)
35+
if err != nil {
36+
return api.Whitelist{}, err
37+
}
38+
res, reqErr := c.Request("POST", u, body)
39+
if reqErr != nil && !deis.IsErrAPIMismatch(reqErr) {
40+
return api.Whitelist{}, reqErr
41+
}
42+
defer res.Body.Close()
43+
44+
d := api.Whitelist{}
45+
if err = json.NewDecoder(res.Body).Decode(&d); err != nil {
46+
return api.Whitelist{}, err
47+
}
48+
49+
return d, reqErr
50+
}
51+
52+
// Delete removes addresses from an app's whitelist.
53+
func Delete(c *deis.Client, appID string, addresses []string) error {
54+
u := fmt.Sprintf("/v2/apps/%s/whitelist/", appID)
55+
56+
req := api.Whitelist{Addresses: addresses}
57+
body, err := json.Marshal(req)
58+
if err != nil {
59+
return err
60+
}
61+
62+
_, reqErr := c.Request("DELETE", u, body)
63+
if reqErr != nil && !deis.IsErrAPIMismatch(reqErr) {
64+
return reqErr
65+
}
66+
return nil
67+
}

whitelist/whitelist_test.go

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package whitelist
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"net/http"
7+
"net/http/httptest"
8+
"reflect"
9+
"testing"
10+
11+
deis "github.com/deis/controller-sdk-go"
12+
"github.com/deis/controller-sdk-go/api"
13+
)
14+
15+
const whitelistFixture string = `
16+
{
17+
"addresses": ["1.2.3.4", "0.0.0.0/0"]
18+
}`
19+
20+
const whitelistCreateExpected string = `{"addresses":["1.2.3.4","0.0.0.0/0"]}`
21+
22+
type fakeHTTPServer struct{}
23+
24+
func (fakeHTTPServer) ServeHTTP(res http.ResponseWriter, req *http.Request) {
25+
res.Header().Add("DEIS_API_VERSION", deis.APIVersion)
26+
27+
if req.URL.Path == "/v2/apps/example-go/whitelist/" && req.Method == "GET" {
28+
res.Write([]byte(whitelistFixture))
29+
return
30+
}
31+
32+
if req.URL.Path == "/v2/apps/example-go/whitelist/" && req.Method == "POST" {
33+
body, err := ioutil.ReadAll(req.Body)
34+
35+
if err != nil {
36+
fmt.Println(err)
37+
res.WriteHeader(http.StatusInternalServerError)
38+
res.Write(nil)
39+
}
40+
41+
if string(body) != whitelistCreateExpected {
42+
fmt.Printf("Expected '%s', Got '%s'\n", whitelistCreateExpected, body)
43+
res.WriteHeader(http.StatusInternalServerError)
44+
res.Write(nil)
45+
return
46+
}
47+
48+
res.WriteHeader(http.StatusCreated)
49+
res.Write([]byte(whitelistFixture))
50+
return
51+
}
52+
53+
if req.URL.Path == "/v2/apps/example-go/whitelist/" && req.Method == "DELETE" {
54+
res.WriteHeader(http.StatusNoContent)
55+
res.Write([]byte(whitelistFixture))
56+
return
57+
}
58+
59+
if req.URL.Path == "/v2/apps/invalidjson-test/whitelist/" && req.Method == "POST" {
60+
res.WriteHeader(http.StatusCreated)
61+
res.Write([]byte(`"addresses": "test"`))
62+
return
63+
}
64+
65+
if req.URL.Path == "/v2/apps/invalidjson-test/whitelist/" && req.Method == "GET" {
66+
res.Write([]byte(`"addresses": "test"`))
67+
return
68+
}
69+
70+
fmt.Printf("Unrecognized URL %s\n", req.URL)
71+
res.WriteHeader(http.StatusNotFound)
72+
res.Write(nil)
73+
}
74+
75+
func TestWhitelistList(t *testing.T) {
76+
t.Parallel()
77+
78+
expected := api.Whitelist{
79+
Addresses: []string{"1.2.3.4", "0.0.0.0/0"},
80+
}
81+
82+
handler := fakeHTTPServer{}
83+
server := httptest.NewServer(handler)
84+
defer server.Close()
85+
86+
deis, err := deis.New(false, server.URL, "abc")
87+
if err != nil {
88+
t.Fatal(err)
89+
}
90+
91+
actual, err := List(deis, "example-go")
92+
93+
if err != nil {
94+
t.Fatal(err)
95+
}
96+
97+
if !reflect.DeepEqual(expected, actual) {
98+
t.Error(fmt.Errorf("Expected %v, Got %v", expected, actual))
99+
}
100+
}
101+
102+
func TestWhitelistAdd(t *testing.T) {
103+
t.Parallel()
104+
105+
expected := api.Whitelist{
106+
Addresses: []string{"1.2.3.4", "0.0.0.0/0"},
107+
}
108+
109+
handler := fakeHTTPServer{}
110+
server := httptest.NewServer(handler)
111+
defer server.Close()
112+
113+
deis, err := deis.New(false, server.URL, "abc")
114+
if err != nil {
115+
t.Fatal(err)
116+
}
117+
118+
actual, err := Add(deis, "example-go", []string{"1.2.3.4", "0.0.0.0/0"})
119+
120+
if err != nil {
121+
t.Fatal(err)
122+
}
123+
124+
if !reflect.DeepEqual(expected, actual) {
125+
t.Error(fmt.Errorf("Expected %v, Got %v", expected, actual))
126+
}
127+
}
128+
129+
func TestWhitelistRemove(t *testing.T) {
130+
t.Parallel()
131+
132+
handler := fakeHTTPServer{}
133+
server := httptest.NewServer(handler)
134+
defer server.Close()
135+
136+
deis, err := deis.New(false, server.URL, "abc")
137+
if err != nil {
138+
t.Fatal(err)
139+
}
140+
141+
if err = Delete(deis, "example-go", []string{"1.2.3.4"}); err != nil {
142+
t.Fatal(err)
143+
}
144+
}
145+
146+
func TestAppSettingsInvalidJson(t *testing.T) {
147+
t.Parallel()
148+
149+
handler := fakeHTTPServer{}
150+
server := httptest.NewServer(&handler)
151+
defer server.Close()
152+
153+
deis, err := deis.New(false, server.URL, "abc")
154+
if err != nil {
155+
t.Fatal(err)
156+
}
157+
158+
_, err = List(deis, "invalidjson-test")
159+
expected := "json: cannot unmarshal string into Go value of type api.Whitelist"
160+
if err == nil || !reflect.DeepEqual(expected, err.Error()) {
161+
t.Errorf("Expected %v, Got %v", expected, err)
162+
}
163+
164+
_, err = Add(deis, "invalidjson-test", []string{"1.2.3.4"})
165+
if err == nil || !reflect.DeepEqual(expected, err.Error()) {
166+
t.Errorf("Expected %v, Got %v", expected, err)
167+
}
168+
}

0 commit comments

Comments
 (0)