Skip to content

Commit 0f37d00

Browse files
committed
Merge pull request #391 from helgi/fix_213
fix(auth): return a 409 if a user is cancelled that has apps
2 parents 356a999 + 223f097 commit 0f37d00

4 files changed

Lines changed: 58 additions & 3 deletions

File tree

client/cmd/auth.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"net/url"
77
"os"
8+
"reflect"
89
"strings"
910
"syscall"
1011

@@ -241,8 +242,11 @@ func Cancel(username string, password string, yes bool) error {
241242
}
242243

243244
err = auth.Delete(c, username)
244-
245-
if err != nil {
245+
cleanup := fmt.Errorf("\n%s %s\n\n", "409", "Conflict")
246+
if reflect.DeepEqual(err, cleanup) {
247+
fmt.Printf("%s still has application associated with it. Transfer ownership or delete them first\n", username)
248+
return nil
249+
} else if err != nil {
246250
return err
247251
}
248252

client/controller/models/auth/auth_test.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"net/http"
77
"net/http/httptest"
88
"net/url"
9+
"reflect"
910
"testing"
1011

1112
"github.com/deis/workflow/client/controller/client"
@@ -18,6 +19,7 @@ const passwdExpected string = `{"username":"test","password":"old","new_password
1819
const regenAllExpected string = `{"all":true}`
1920
const regenUserExpected string = `{"username":"test"}`
2021
const cancelUserExpected string = `{"username":"foo"}`
22+
const cancelAdminExpected string = `{"username":"admin"}`
2123

2224
type fakeHTTPServer struct {
2325
regenBodyEmpty bool
@@ -131,7 +133,12 @@ func (f *fakeHTTPServer) ServeHTTP(res http.ResponseWriter, req *http.Request) {
131133
res.Write(nil)
132134
}
133135

134-
if string(body) == cancelUserExpected && !f.cancelUsername {
136+
if string(body) == cancelAdminExpected && !f.cancelUsername {
137+
f.cancelUsername = true
138+
res.WriteHeader(http.StatusConflict)
139+
res.Write(nil)
140+
return
141+
} else if string(body) == cancelUserExpected && !f.cancelUsername {
135142
f.cancelUsername = true
136143
res.WriteHeader(http.StatusNoContent)
137144
res.Write(nil)
@@ -251,6 +258,31 @@ func TestDelete(t *testing.T) {
251258
}
252259
}
253260

261+
func TestDeleteUserApp(t *testing.T) {
262+
t.Parallel()
263+
264+
handler := fakeHTTPServer{cancelUsername: false, cancelEmpty: false}
265+
server := httptest.NewServer(&handler)
266+
defer server.Close()
267+
268+
u, err := url.Parse(server.URL)
269+
270+
if err != nil {
271+
t.Fatal(err)
272+
}
273+
274+
httpClient := client.CreateHTTPClient(false)
275+
client := client.Client{HTTPClient: httpClient, ControllerURL: *u}
276+
277+
err = Delete(&client, "admin")
278+
// should be a 409 Conflict
279+
280+
expected := fmt.Errorf("\n%s %s\n\n", "409", "Conflict")
281+
if reflect.DeepEqual(err, expected) == false {
282+
t.Errorf("got '%s' but expected '%s'", err, expected)
283+
}
284+
}
285+
254286
func TestRegenerate(t *testing.T) {
255287
t.Parallel()
256288

rootfs/api/tests/test_auth.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,20 @@ def test_cancel(self):
221221
HTTP_AUTHORIZATION='token {}'.format(self.admin_token))
222222
self.assertEqual(response.status_code, 204)
223223

224+
# user can not be deleted if it has an app attached to it
225+
response = self.client.post(
226+
'/v2/apps',
227+
HTTP_AUTHORIZATION='token {}'.format(self.admin_token)
228+
)
229+
self.assertEqual(response.status_code, 201)
230+
app_id = response.data['id'] # noqa
231+
self.assertIn('id', response.data)
232+
233+
response = self.client.delete(url, json.dumps({'username': str(self.admin)}),
234+
content_type='application/json',
235+
HTTP_AUTHORIZATION='token {}'.format(self.admin_token))
236+
self.assertEqual(response.status_code, 409)
237+
224238
def test_passwd(self):
225239
"""Test that a registered user can change the password."""
226240
# test registration workflow

rootfs/api/views.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ def destroy(self, request, **kwargs):
7777
else:
7878
raise PermissionDenied()
7979

80+
# A user can not be removed without apps changing ownership first
81+
if len(models.App.objects.filter(owner=target_obj)) > 0:
82+
msg = '{} still has applications assigned. Delete or transfer ownership'.format(str(target_obj)) # noqa
83+
return Response({'detail': msg}, status=status.HTTP_409_CONFLICT)
84+
8085
target_obj.delete()
8186
return Response(status=status.HTTP_204_NO_CONTENT)
8287

0 commit comments

Comments
 (0)