Skip to content
This repository has been archived by the owner on May 19, 2020. It is now read-only.

Commit

Permalink
Merge pull request #1139 from 18F/js-fix-multiple-account-on-invite
Browse files Browse the repository at this point in the history
Check for user by e-mail before sending invite
  • Loading branch information
jcscottiii authored Jun 27, 2017
2 parents 349a273 + cac4535 commit 6d3136e
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 47 deletions.
87 changes: 53 additions & 34 deletions controllers/uaa.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"net/http"
"net/http/httptest"
"net/url"

"github.com/gocraft/web"
)
Expand Down Expand Up @@ -211,37 +212,36 @@ func (c *UAAContext) InviteUserToOrg(rw web.ResponseWriter, req *web.Request) {
return
}

// Try to invite the user to UAA.
inviteResponse, err := c.InviteUAAuser(inviteUserToOrgRequest)

var getUserResp GetUAAUserResponse
getUserResp, err = c.GetUAAUserByEmail(inviteUserToOrgRequest.Email)
if err != nil {
err.writeTo(rw)
return
}
if !getUserResp.Verified {
// Try to invite the user to UAA.
inviteResponse, err := c.InviteUAAuser(inviteUserToOrgRequest)
if err != nil {
err.writeTo(rw)
return
}

// If we don't have a successful invite, we return an error.
if len(inviteResponse.NewInvites) < 1 {
rw.WriteHeader(http.StatusInternalServerError)
rw.Write([]byte("{\"status\": \"failure\", " +
"\"data\": \"no successful invites created.\"}"))
return
}
userInvite := inviteResponse.NewInvites[0]

// Next try to create the user in CF
err = c.CreateCFuser(userInvite)
if err != nil {
err.writeTo(rw)
return
}
// If we don't have a successful invite, we return an error.
if len(inviteResponse.NewInvites) < 1 {
rw.WriteHeader(http.StatusInternalServerError)
rw.Write([]byte("{\"status\": \"failure\", " +
"\"data\": \"no successful invites created.\"}"))
return
}
userInvite := inviteResponse.NewInvites[0]

verifyResp, err := c.GetUAAUser(userInvite)
if err != nil {
err.writeTo(rw)
return
}
// Next try to create the user in CF
err = c.CreateCFuser(userInvite)
if err != nil {
err.writeTo(rw)
return
}

if verifyResp.Verified == false {
// Trigger the e-mail invite.
err = c.TriggerInvite(inviteEmailRequest{
Email: userInvite.Email,
Expand All @@ -256,29 +256,48 @@ func (c *UAAContext) InviteUserToOrg(rw web.ResponseWriter, req *web.Request) {
rw.WriteHeader(http.StatusOK)
rw.Write([]byte(fmt.Sprintf("{\"status\": \"success\", "+
"\"userGuid\": \"%s\", "+
"\"verified\": %t}", userInvite.UserID, verifyResp.Verified)))
"\"verified\": %t}", getUserResp.ID, getUserResp.Verified)))
}

// GetUAAUser will query UAA for a particular user.
func (c *UAAContext) GetUAAUser(userInvite NewInvite) (
verifyResp GetUAAUserResponse, err *UaaError) {
reqURL := fmt.Sprintf("%s%s%s", "/Users/", userInvite.UserID, "")
// ListUAAUserResponse is the response representation of the User list query.
// https://docs.cloudfoundry.org/api/uaa/#list63
type ListUAAUserResponse struct {
Resources []GetUAAUserResponse `json:"resources"`
}

// GetUAAUserByEmail will query UAA for user(s) by e-mail.
// Only return one user result.
// Special cases:
// If multiple are found, an empty response is returned.
// If none are found, an empty response is returned.
// Both special cases return no error.
func (c *UAAContext) GetUAAUserByEmail(email string) (
userResponse GetUAAUserResponse, err *UaaError) {
filterQuery := fmt.Sprintf("email eq \"%s\"", email)
reqURL := fmt.Sprintf("/Users?filter=%s", url.QueryEscape(filterQuery))
reqVerify, _ := http.NewRequest("GET", reqURL, nil)
w := httptest.NewRecorder()
c.uaaProxy(w, reqVerify, reqURL, true)
// It will always return StatusOK even if it returns an empty resources list.
if w.Code != http.StatusOK {
resp := w.Result()
body, _ := ioutil.ReadAll(resp.Body)
err = &UaaError{http.StatusInternalServerError,
[]byte("{\"status\": \"failure\", \"data\": \"" +
"unable to create user in UAA database.\", \"proxy-data\": \"" +
string(body) + "\"}")}
"unable to find user.\"}")}
return
}
resp := w.Result()
var listUsersResponse ListUAAUserResponse
err = readBodyToStruct(resp.Body, &listUsersResponse)
if err != nil {
return
}
// In the case we don't find anything or find duplicates, just return.
if len(listUsersResponse.Resources) != 1 {
return
}

err = readBodyToStruct(resp.Body, &verifyResp)
// In the case we find one, let's return that.
userResponse = listUsersResponse.Resources[0]
return
}

Expand Down
35 changes: 24 additions & 11 deletions controllers/uaa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,16 @@ var inviteUsersTest = []BasicProxyTest{
Handlers: []Handler{
{
RequestMethod: "POST",
ExpectedPath: "/invite_users",
ExpectedPath: "/invite_users?redirect_uri=https://hostname",
Response: "{\"new_invites\": [{\"email\": \"[email protected]\", \"userId\": \"user-guid\"}]}",
ResponseCode: http.StatusOK,
},
{
RequestMethod: "GET",
ExpectedPath: "/Users?filter=email+eq+%22test%40example.com%22",
ResponseCode: http.StatusOK,
Response: "{\"resources\": [{\"active\": true, \"verified\": false, \"id\": \"user-guid\", \"externalId\": \"[email protected]\" }]}",
},
{
RequestMethod: "GET",
ExpectedPath: "/Users/user-guid",
Expand Down Expand Up @@ -112,10 +118,16 @@ var inviteUsersTest = []BasicProxyTest{
Handlers: []Handler{
{
RequestMethod: "POST",
ExpectedPath: "/invite_users",
ExpectedPath: "/invite_users?redirect_uri=https://hostname",
Response: "{\"new_invites\": [{\"inviteLink\": \"http://some.link\", \"userId\": \"user-guid\"}]}",
ResponseCode: http.StatusOK,
},
{
RequestMethod: "GET",
ExpectedPath: "/Users?filter=email+eq+%22test%40example.com%22",
ResponseCode: http.StatusOK,
Response: "{\"resources\": [{\"active\": true, \"verified\": false, \"id\": \"user-guid\", \"externalId\": \"[email protected]\" }]}",
},
{
RequestMethod: "GET",
ExpectedPath: "/Users/user-guid",
Expand Down Expand Up @@ -145,10 +157,16 @@ var inviteUsersTest = []BasicProxyTest{
Handlers: []Handler{
{
RequestMethod: "POST",
ExpectedPath: "/invite_users",
ExpectedPath: "/invite_users?redirect_uri=https://hostname",
Response: "{\"new_invites\": [{\"email\": \"[email protected]\", \"userId\": \"user-guid\", \"inviteLink\": \"http://some.link\"}]}",
ResponseCode: http.StatusOK,
},
{
RequestMethod: "GET",
ExpectedPath: "/Users?filter=email+eq+%22test%40example.com%22",
ResponseCode: http.StatusOK,
Response: "{\"resources\": [{\"active\": true, \"verified\": false, \"id\": \"user-guid\", \"externalId\": \"[email protected]\" }]}",
},
{
RequestMethod: "GET",
ExpectedPath: "/Users/user-guid",
Expand Down Expand Up @@ -178,20 +196,15 @@ var inviteUsersTest = []BasicProxyTest{
Handlers: []Handler{
{
RequestMethod: "POST",
ExpectedPath: "/invite_users",
ExpectedPath: "/invite_users?redirect_uri=https://hostname",
Response: "{\"new_invites\": [{\"email\": \"[email protected]\", \"userId\": \"user-guid\", \"inviteLink\": \"http://some.link\"}]}",
ResponseCode: http.StatusOK,
},
{
RequestMethod: "GET",
ExpectedPath: "/Users/user-guid",
ExpectedPath: "/Users?filter=email+eq+%22test%40example.com%22",
ResponseCode: http.StatusOK,
Response: "{\"active\": true, \"verified\": true, \"id\": \"user-guid\", \"externalId\": \"[email protected]\" }",
},
{
RequestMethod: "POST",
ExpectedPath: "/v2/users",
ResponseCode: http.StatusCreated,
Response: "{\"resources\": [{\"active\": true, \"verified\": true, \"id\": \"user-guid\", \"externalId\": \"[email protected]\" }]}",
},
},
},
Expand Down
4 changes: 2 additions & 2 deletions helpers/testhelpers/testhelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ func CreateExternalServerForPrivileged(t *testing.T, test BasicProxyTest) *httpt
} else {
foundHandler := false
for _, handler := range test.Handlers {
if r.URL.Path == handler.ExpectedPath && r.Method == handler.RequestMethod {
if r.URL.RequestURI() == handler.ExpectedPath && r.Method == handler.RequestMethod {
w.WriteHeader(handler.ResponseCode)
fmt.Fprintln(w, handler.Response)
foundHandler = true
Expand All @@ -226,7 +226,7 @@ func CreateExternalServerForPrivileged(t *testing.T, test BasicProxyTest) *httpt
}
if !foundHandler {
t.Errorf("Test name: (%s) Server received method %s\n", test.TestName, r.Method)
t.Errorf("Debug path: Got stuck on (%s) after frontend sent (%s)\n", r.URL.Path, test.RequestPath)
t.Errorf("Debug path: Got stuck on (%s) after frontend sent (%s)\n", r.URL.RequestURI(), test.RequestPath)
t.Errorf("Tried the following handlers %+v\n", test.Handlers)
}
}
Expand Down

0 comments on commit 6d3136e

Please sign in to comment.