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

Check for user by e-mail before sending invite #1139

Merged
merged 1 commit into from
Jun 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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