github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/registration_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package apiserver_test 5 6 import ( 7 "encoding/base64" 8 "encoding/json" 9 "fmt" 10 "io/ioutil" 11 "net/http" 12 "strings" 13 14 jc "github.com/juju/testing/checkers" 15 "github.com/juju/testing/httptesting" 16 "github.com/juju/utils" 17 "golang.org/x/crypto/nacl/secretbox" 18 gc "gopkg.in/check.v1" 19 20 "github.com/juju/juju/apiserver/params" 21 "github.com/juju/juju/state" 22 ) 23 24 type registrationSuite struct { 25 authHttpSuite 26 bob *state.User 27 } 28 29 var _ = gc.Suite(®istrationSuite{}) 30 31 func (s *registrationSuite) SetUpTest(c *gc.C) { 32 s.authHttpSuite.SetUpTest(c) 33 bob, err := s.BackingState.AddUserWithSecretKey("bob", "", "admin") 34 c.Assert(err, jc.ErrorIsNil) 35 s.bob = bob 36 } 37 38 func (s *registrationSuite) assertErrorResponse(c *gc.C, resp *http.Response, expCode int, expError string) { 39 body := assertResponse(c, resp, expCode, params.ContentTypeJSON) 40 var result params.ErrorResult 41 s.unmarshal(c, body, &result) 42 c.Assert(result.Error, gc.NotNil) 43 c.Assert(result.Error, gc.Matches, expError) 44 } 45 46 func (s *registrationSuite) assertResponse(c *gc.C, resp *http.Response) params.SecretKeyLoginResponse { 47 body := assertResponse(c, resp, http.StatusOK, params.ContentTypeJSON) 48 var response params.SecretKeyLoginResponse 49 s.unmarshal(c, body, &response) 50 return response 51 } 52 53 func (*registrationSuite) unmarshal(c *gc.C, body []byte, out interface{}) { 54 err := json.Unmarshal(body, out) 55 c.Assert(err, jc.ErrorIsNil, gc.Commentf("body: %s", body)) 56 } 57 58 func (s *registrationSuite) registrationURL(c *gc.C) string { 59 url := s.baseURL(c) 60 url.Path = "/register" 61 return url.String() 62 } 63 64 func (s *registrationSuite) TestRegister(c *gc.C) { 65 // Ensure we cannot log in with the password yet. 66 const password = "hunter2" 67 c.Assert(s.bob.PasswordValid(password), jc.IsFalse) 68 69 validNonce := []byte(strings.Repeat("X", 24)) 70 secretKey := s.bob.SecretKey() 71 ciphertext := s.sealBox( 72 c, validNonce, secretKey, fmt.Sprintf(`{"password": "%s"}`, password), 73 ) 74 resp := httptesting.Do(c, httptesting.DoRequestParams{ 75 Do: utils.GetNonValidatingHTTPClient().Do, 76 URL: s.registrationURL(c), 77 Method: "POST", 78 JSONBody: ¶ms.SecretKeyLoginRequest{ 79 User: "user-bob", 80 Nonce: validNonce, 81 PayloadCiphertext: ciphertext, 82 }, 83 }) 84 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 85 defer resp.Body.Close() 86 87 // It should be possible to log in as bob with the 88 // password "hunter2" now, and there should be no 89 // secret key any longer. 90 err := s.bob.Refresh() 91 c.Assert(err, jc.ErrorIsNil) 92 c.Assert(s.bob.PasswordValid(password), jc.IsTrue) 93 c.Assert(s.bob.SecretKey(), gc.IsNil) 94 95 var response params.SecretKeyLoginResponse 96 bodyData, err := ioutil.ReadAll(resp.Body) 97 c.Assert(err, jc.ErrorIsNil) 98 err = json.Unmarshal(bodyData, &response) 99 c.Assert(err, jc.ErrorIsNil) 100 c.Assert(response.Nonce, gc.HasLen, len(validNonce)) 101 plaintext := s.openBox(c, response.PayloadCiphertext, response.Nonce, secretKey) 102 103 var responsePayload params.SecretKeyLoginResponsePayload 104 err = json.Unmarshal(plaintext, &responsePayload) 105 c.Assert(err, jc.ErrorIsNil) 106 c.Assert(responsePayload.CACert, gc.Equals, s.BackingState.CACert()) 107 model, err := s.BackingState.Model() 108 c.Assert(err, jc.ErrorIsNil) 109 c.Assert(responsePayload.ControllerUUID, gc.Equals, model.ControllerUUID()) 110 } 111 112 func (s *registrationSuite) TestRegisterInvalidMethod(c *gc.C) { 113 httptesting.AssertJSONCall(c, httptesting.JSONCallParams{ 114 Do: utils.GetNonValidatingHTTPClient().Do, 115 URL: s.registrationURL(c), 116 Method: "GET", 117 ExpectStatus: http.StatusMethodNotAllowed, 118 ExpectBody: ¶ms.ErrorResult{ 119 Error: ¶ms.Error{ 120 Message: `unsupported method: "GET"`, 121 Code: params.CodeMethodNotAllowed, 122 }, 123 }, 124 }) 125 } 126 127 func (s *registrationSuite) TestRegisterInvalidFormat(c *gc.C) { 128 s.testInvalidRequest( 129 c, "[]", "json: cannot unmarshal array into Go value of type params.SecretKeyLoginRequest", "", 130 http.StatusInternalServerError, 131 ) 132 } 133 134 func (s *registrationSuite) TestRegisterInvalidUserTag(c *gc.C) { 135 s.testInvalidRequest( 136 c, `{"user": "service-bob"}`, `"service-bob" is not a valid user tag`, "", 137 http.StatusInternalServerError, 138 ) 139 } 140 141 func (s *registrationSuite) TestRegisterInvalidNonce(c *gc.C) { 142 s.testInvalidRequest( 143 c, `{"user": "user-bob", "nonce": ""}`, `nonce not valid`, "", 144 http.StatusInternalServerError, 145 ) 146 } 147 148 func (s *registrationSuite) TestRegisterInvalidCiphertext(c *gc.C) { 149 validNonce := []byte(strings.Repeat("X", 24)) 150 s.testInvalidRequest(c, 151 fmt.Sprintf( 152 `{"user": "user-bob", "nonce": "%s"}`, 153 base64.StdEncoding.EncodeToString(validNonce), 154 ), `secret key not valid`, "", 155 http.StatusInternalServerError, 156 ) 157 } 158 159 func (s *registrationSuite) TestRegisterNoSecretKey(c *gc.C) { 160 err := s.bob.SetPassword("anything") 161 c.Assert(err, jc.ErrorIsNil) 162 validNonce := []byte(strings.Repeat("X", 24)) 163 s.testInvalidRequest(c, 164 fmt.Sprintf( 165 `{"user": "user-bob", "nonce": "%s"}`, 166 base64.StdEncoding.EncodeToString(validNonce), 167 ), `secret key for user "bob" not found`, params.CodeNotFound, 168 http.StatusNotFound, 169 ) 170 } 171 172 func (s *registrationSuite) TestRegisterInvalidRequestPayload(c *gc.C) { 173 validNonce := []byte(strings.Repeat("X", 24)) 174 ciphertext := s.sealBox(c, validNonce, s.bob.SecretKey(), "[]") 175 s.testInvalidRequest(c, 176 fmt.Sprintf( 177 `{"user": "user-bob", "nonce": "%s", "ciphertext": "%s"}`, 178 base64.StdEncoding.EncodeToString(validNonce), 179 base64.StdEncoding.EncodeToString(ciphertext), 180 ), 181 `cannot unmarshal payload: json: cannot unmarshal array into Go value of type params.SecretKeyLoginRequestPayload`, "", 182 http.StatusInternalServerError, 183 ) 184 } 185 186 func (s *registrationSuite) testInvalidRequest(c *gc.C, requestBody, errorMessage, errorCode string, statusCode int) { 187 httptesting.AssertJSONCall(c, httptesting.JSONCallParams{ 188 Do: utils.GetNonValidatingHTTPClient().Do, 189 URL: s.registrationURL(c), 190 Method: "POST", 191 Body: strings.NewReader(requestBody), 192 ExpectStatus: statusCode, 193 ExpectBody: ¶ms.ErrorResult{ 194 Error: ¶ms.Error{Message: errorMessage, Code: errorCode}, 195 }, 196 }) 197 } 198 199 func (s *registrationSuite) sealBox(c *gc.C, nonce, key []byte, message string) []byte { 200 var nonceArray [24]byte 201 var keyArray [32]byte 202 c.Assert(copy(nonceArray[:], nonce), gc.Equals, len(nonceArray)) 203 c.Assert(copy(keyArray[:], key), gc.Equals, len(keyArray)) 204 return secretbox.Seal(nil, []byte(message), &nonceArray, &keyArray) 205 } 206 207 func (s *registrationSuite) openBox(c *gc.C, ciphertext, nonce, key []byte) []byte { 208 var nonceArray [24]byte 209 var keyArray [32]byte 210 c.Assert(copy(nonceArray[:], nonce), gc.Equals, len(nonceArray)) 211 c.Assert(copy(keyArray[:], key), gc.Equals, len(keyArray)) 212 message, ok := secretbox.Open(nil, ciphertext, &nonceArray, &keyArray) 213 c.Assert(ok, jc.IsTrue) 214 return message 215 }