Merge pull request #331 from Tecsisa/184-resend-email-invitation
add support for resend an invite email
This commit is contained in:
commit
c92aae647c
8 changed files with 595 additions and 13 deletions
|
@ -45,8 +45,9 @@ var (
|
|||
},
|
||||
{
|
||||
User: user.User{
|
||||
ID: "ID-2",
|
||||
Email: "Email-2@example.com",
|
||||
ID: "ID-2",
|
||||
Email: "Email-2@example.com",
|
||||
EmailVerified: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -582,6 +583,184 @@ func TestDisableUser(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestResendEmailInvitation(t *testing.T) {
|
||||
tests := []struct {
|
||||
req schema.ResendEmailInvitationRequest
|
||||
cantEmail bool
|
||||
userID string
|
||||
email string
|
||||
token string
|
||||
|
||||
wantResponse schema.ResendEmailInvitationResponse
|
||||
wantCode int
|
||||
}{
|
||||
{
|
||||
|
||||
req: schema.ResendEmailInvitationRequest{
|
||||
RedirectURL: testRedirectURL.String(),
|
||||
},
|
||||
|
||||
userID: "ID-3",
|
||||
email: "Email-3@example.com",
|
||||
token: userGoodToken,
|
||||
|
||||
wantResponse: schema.ResendEmailInvitationResponse{
|
||||
EmailSent: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
req: schema.ResendEmailInvitationRequest{
|
||||
RedirectURL: testRedirectURL.String(),
|
||||
},
|
||||
|
||||
userID: "ID-3",
|
||||
email: "Email-3@example.com",
|
||||
cantEmail: true,
|
||||
token: userGoodToken,
|
||||
|
||||
wantResponse: schema.ResendEmailInvitationResponse{
|
||||
ResetPasswordLink: testResetPasswordURL.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
req: schema.ResendEmailInvitationRequest{
|
||||
RedirectURL: "http://scammers.com",
|
||||
},
|
||||
|
||||
userID: "ID-3",
|
||||
email: "Email-3@example.com",
|
||||
token: userGoodToken,
|
||||
|
||||
wantCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
|
||||
req: schema.ResendEmailInvitationRequest{
|
||||
RedirectURL: testRedirectURL.String(),
|
||||
},
|
||||
|
||||
userID: "ID-2",
|
||||
email: "Email-2@example.com",
|
||||
token: userGoodToken,
|
||||
|
||||
wantCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
req: schema.ResendEmailInvitationRequest{
|
||||
RedirectURL: testRedirectURL.String(),
|
||||
},
|
||||
|
||||
userID: "ID-3",
|
||||
email: "Email-3@example.com",
|
||||
token: userBadTokenClientNotAdmin,
|
||||
|
||||
wantCode: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
req: schema.ResendEmailInvitationRequest{
|
||||
RedirectURL: testRedirectURL.String(),
|
||||
},
|
||||
|
||||
userID: "ID-3",
|
||||
email: "Email-3@example.com",
|
||||
token: userBadClientID,
|
||||
|
||||
wantCode: http.StatusUnauthorized,
|
||||
},
|
||||
{
|
||||
req: schema.ResendEmailInvitationRequest{
|
||||
RedirectURL: testRedirectURL.String(),
|
||||
},
|
||||
|
||||
userID: "ID-3",
|
||||
email: "Email-3@example.com",
|
||||
token: userBadTokenExpired,
|
||||
|
||||
wantCode: http.StatusUnauthorized,
|
||||
},
|
||||
{
|
||||
req: schema.ResendEmailInvitationRequest{
|
||||
RedirectURL: testRedirectURL.String(),
|
||||
},
|
||||
|
||||
userID: "ID-3",
|
||||
email: "Email-3@example.com",
|
||||
token: userBadTokenDisabled,
|
||||
|
||||
wantCode: http.StatusUnauthorized,
|
||||
},
|
||||
{
|
||||
req: schema.ResendEmailInvitationRequest{
|
||||
RedirectURL: testRedirectURL.String(),
|
||||
},
|
||||
|
||||
userID: "ID-3",
|
||||
email: "Email-3@example.com",
|
||||
token: userBadTokenNotAdmin,
|
||||
|
||||
wantCode: http.StatusUnauthorized,
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
func() {
|
||||
f := makeUserAPITestFixtures()
|
||||
defer f.close()
|
||||
f.trans.Token = tt.token
|
||||
f.emailer.cantEmail = tt.cantEmail
|
||||
|
||||
page, err := f.client.Users.ResendEmailInvitation(tt.userID, &tt.req).Do()
|
||||
if tt.wantCode != 0 {
|
||||
if err == nil {
|
||||
t.Errorf("case %d: err was nil", i)
|
||||
return
|
||||
}
|
||||
gErr, ok := err.(*googleapi.Error)
|
||||
if !ok {
|
||||
t.Errorf("case %d: not a googleapi Error: %q", i, err)
|
||||
return
|
||||
}
|
||||
|
||||
if gErr.Code != tt.wantCode {
|
||||
t.Errorf("case %d: want=%d, got=%d", i, tt.wantCode, gErr.Code)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("case %d: want nil err, got: %v %T ", i, err, err)
|
||||
return
|
||||
}
|
||||
|
||||
if diff := pretty.Compare(tt.wantResponse, page); diff != "" {
|
||||
t.Errorf("case %d: Compare(want, got) = %v", i, diff)
|
||||
return
|
||||
}
|
||||
|
||||
urlParsed, err := url.Parse(tt.req.RedirectURL)
|
||||
if err != nil {
|
||||
t.Errorf("case %d unexpected err: %v", i, err)
|
||||
return
|
||||
}
|
||||
|
||||
wantEmalier := testEmailer{
|
||||
cantEmail: tt.cantEmail,
|
||||
lastEmail: tt.email,
|
||||
lastClientID: "XXX",
|
||||
lastWasInvite: true,
|
||||
lastRedirectURL: *urlParsed,
|
||||
}
|
||||
if diff := pretty.Compare(wantEmalier, f.emailer); diff != "" {
|
||||
t.Errorf("case %d: Compare(want, got) = %v", i, diff)
|
||||
return
|
||||
}
|
||||
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
type testEmailer struct {
|
||||
cantEmail bool
|
||||
lastEmail string
|
||||
|
|
|
@ -59,6 +59,27 @@ __Version:__ v1
|
|||
}
|
||||
```
|
||||
|
||||
### ResendEmailInvitationRequest
|
||||
|
||||
|
||||
|
||||
```
|
||||
{
|
||||
redirectURL: string
|
||||
}
|
||||
```
|
||||
|
||||
### ResendEmailInvitationResponse
|
||||
|
||||
|
||||
|
||||
```
|
||||
{
|
||||
emailSent: boolean,
|
||||
resetPasswordLink: string
|
||||
}
|
||||
```
|
||||
|
||||
### User
|
||||
|
||||
|
||||
|
@ -303,3 +324,30 @@ __Version:__ v1
|
|||
| default | Unexpected error | |
|
||||
|
||||
|
||||
### POST /users/{id}/resend-invitation
|
||||
|
||||
> __Summary__
|
||||
|
||||
> ResendEmailInvitation Users
|
||||
|
||||
> __Description__
|
||||
|
||||
> Resend invitation email to an existing user with unverified email.
|
||||
|
||||
|
||||
> __Parameters__
|
||||
|
||||
> |Name|Located in|Description|Required|Type|
|
||||
|:-----|:-----|:-----|:-----|:-----|
|
||||
| id | path | | Yes | string |
|
||||
| | body | | Yes | [ResendEmailInvitationRequest](#resendemailinvitationrequest) |
|
||||
|
||||
|
||||
> __Responses__
|
||||
|
||||
> |Code|Description|Type|
|
||||
|:-----|:-----|:-----|
|
||||
| 200 | | [ResendEmailInvitationResponse](#resendemailinvitationresponse) |
|
||||
| default | Unexpected error | |
|
||||
|
||||
|
||||
|
|
|
@ -14,13 +14,12 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"google.golang.org/api/googleapi"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/api/googleapi"
|
||||
)
|
||||
|
||||
// Always reference these packages, just in case the auto-generated code
|
||||
|
@ -103,6 +102,16 @@ type Error struct {
|
|||
Error_description string `json:"error_description,omitempty"`
|
||||
}
|
||||
|
||||
type ResendEmailInvitationRequest struct {
|
||||
RedirectURL string `json:"redirectURL,omitempty"`
|
||||
}
|
||||
|
||||
type ResendEmailInvitationResponse struct {
|
||||
EmailSent bool `json:"emailSent,omitempty"`
|
||||
|
||||
ResetPasswordLink string `json:"resetPasswordLink,omitempty"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Admin bool `json:"admin,omitempty"`
|
||||
|
||||
|
@ -607,3 +616,87 @@ func (c *UsersListCall) Do() (*UsersResponse, error) {
|
|||
// }
|
||||
|
||||
}
|
||||
|
||||
// method id "dex.User.ResendEmailInvitation":
|
||||
|
||||
type UsersResendEmailInvitationCall struct {
|
||||
s *Service
|
||||
id string
|
||||
resendemailinvitationrequest *ResendEmailInvitationRequest
|
||||
opt_ map[string]interface{}
|
||||
}
|
||||
|
||||
// ResendEmailInvitation: Resend invitation email to an existing user
|
||||
// with unverified email.
|
||||
func (r *UsersService) ResendEmailInvitation(id string, resendemailinvitationrequest *ResendEmailInvitationRequest) *UsersResendEmailInvitationCall {
|
||||
c := &UsersResendEmailInvitationCall{s: r.s, opt_: make(map[string]interface{})}
|
||||
c.id = id
|
||||
c.resendemailinvitationrequest = resendemailinvitationrequest
|
||||
return c
|
||||
}
|
||||
|
||||
// Fields allows partial responses to be retrieved.
|
||||
// See https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
|
||||
// for more information.
|
||||
func (c *UsersResendEmailInvitationCall) Fields(s ...googleapi.Field) *UsersResendEmailInvitationCall {
|
||||
c.opt_["fields"] = googleapi.CombineFields(s)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *UsersResendEmailInvitationCall) Do() (*ResendEmailInvitationResponse, error) {
|
||||
var body io.Reader = nil
|
||||
body, err := googleapi.WithoutDataWrapper.JSONReader(c.resendemailinvitationrequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctype := "application/json"
|
||||
params := make(url.Values)
|
||||
params.Set("alt", "json")
|
||||
if v, ok := c.opt_["fields"]; ok {
|
||||
params.Set("fields", fmt.Sprintf("%v", v))
|
||||
}
|
||||
urls := googleapi.ResolveRelative(c.s.BasePath, "users/{id}/resend-invitation")
|
||||
urls += "?" + params.Encode()
|
||||
req, _ := http.NewRequest("POST", urls, body)
|
||||
googleapi.Expand(req.URL, map[string]string{
|
||||
"id": c.id,
|
||||
})
|
||||
req.Header.Set("Content-Type", ctype)
|
||||
req.Header.Set("User-Agent", "google-api-go-client/0.5")
|
||||
res, err := c.s.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer googleapi.CloseBody(res)
|
||||
if err := googleapi.CheckResponse(res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ret *ResendEmailInvitationResponse
|
||||
if err := json.NewDecoder(res.Body).Decode(&ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
// {
|
||||
// "description": "Resend invitation email to an existing user with unverified email.",
|
||||
// "httpMethod": "POST",
|
||||
// "id": "dex.User.ResendEmailInvitation",
|
||||
// "parameterOrder": [
|
||||
// "id"
|
||||
// ],
|
||||
// "parameters": {
|
||||
// "id": {
|
||||
// "location": "path",
|
||||
// "required": true,
|
||||
// "type": "string"
|
||||
// }
|
||||
// },
|
||||
// "path": "users/{id}/resend-invitation",
|
||||
// "request": {
|
||||
// "$ref": "ResendEmailInvitationRequest"
|
||||
// },
|
||||
// "response": {
|
||||
// "$ref": "ResendEmailInvitationResponse"
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
|
@ -188,6 +188,28 @@ const DiscoveryJSON = `{
|
|||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ResendEmailInvitationRequest": {
|
||||
"id": "UserDisableRequest",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"redirectURL": {
|
||||
"type": "string",
|
||||
"format": "url"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ResendEmailInvitationResponse": {
|
||||
"id": "UserDisableResponse",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"resetPasswordLink": {
|
||||
"type": "string"
|
||||
},
|
||||
"emailSent": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
|
@ -295,6 +317,28 @@ const DiscoveryJSON = `{
|
|||
"response": {
|
||||
"$ref": "UserDisableResponse"
|
||||
}
|
||||
},
|
||||
"ResendEmailInvitation": {
|
||||
"id": "dex.User.ResendEmailInvitation",
|
||||
"description": "Resend invitation email to an existing user with unverified email.",
|
||||
"httpMethod": "POST",
|
||||
"path": "users/{id}/resend-invitation",
|
||||
"parameters": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"location": "path"
|
||||
}
|
||||
},
|
||||
"parameterOrder": [
|
||||
"id"
|
||||
],
|
||||
"request": {
|
||||
"$ref": "ResendEmailInvitationRequest"
|
||||
},
|
||||
"response": {
|
||||
"$ref": "ResendEmailInvitationResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -182,6 +182,28 @@
|
|||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ResendEmailInvitationRequest": {
|
||||
"id": "UserDisableRequest",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"redirectURL": {
|
||||
"type": "string",
|
||||
"format": "url"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ResendEmailInvitationResponse": {
|
||||
"id": "UserDisableResponse",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"resetPasswordLink": {
|
||||
"type": "string"
|
||||
},
|
||||
"emailSent": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
|
@ -289,6 +311,28 @@
|
|||
"response": {
|
||||
"$ref": "UserDisableResponse"
|
||||
}
|
||||
},
|
||||
"ResendEmailInvitation": {
|
||||
"id": "dex.User.ResendEmailInvitation",
|
||||
"description": "Resend invitation email to an existing user with unverified email.",
|
||||
"httpMethod": "POST",
|
||||
"path": "users/{id}/resend-invitation",
|
||||
"parameters": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"location": "path"
|
||||
}
|
||||
},
|
||||
"parameterOrder": [
|
||||
"id"
|
||||
],
|
||||
"request": {
|
||||
"$ref": "ResendEmailInvitationRequest"
|
||||
},
|
||||
"response": {
|
||||
"$ref": "ResendEmailInvitationResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,11 +24,12 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
UsersSubTree = "/users"
|
||||
UsersListEndpoint = addBasePath(UsersSubTree)
|
||||
UsersCreateEndpoint = addBasePath(UsersSubTree)
|
||||
UsersGetEndpoint = addBasePath(UsersSubTree + "/:id")
|
||||
UsersDisableEndpoint = addBasePath(UsersSubTree + "/:id/disable")
|
||||
UsersSubTree = "/users"
|
||||
UsersListEndpoint = addBasePath(UsersSubTree)
|
||||
UsersCreateEndpoint = addBasePath(UsersSubTree)
|
||||
UsersGetEndpoint = addBasePath(UsersSubTree + "/:id")
|
||||
UsersDisableEndpoint = addBasePath(UsersSubTree + "/:id/disable")
|
||||
UsersResendInvitationEndpoint = addBasePath(UsersSubTree + "/:id/resend-invitation")
|
||||
)
|
||||
|
||||
type UserMgmtServer struct {
|
||||
|
@ -55,6 +56,7 @@ func (s *UserMgmtServer) HTTPHandler() http.Handler {
|
|||
r.POST(UsersCreateEndpoint, s.authAPIHandle(s.createUser))
|
||||
r.POST(UsersDisableEndpoint, s.authAPIHandle(s.disableUser))
|
||||
r.GET(UsersGetEndpoint, s.authAPIHandle(s.getUser))
|
||||
r.POST(UsersResendInvitationEndpoint, s.authAPIHandle(s.resendInvitationEmail))
|
||||
return r
|
||||
}
|
||||
|
||||
|
@ -161,6 +163,34 @@ func (s *UserMgmtServer) disableUser(w http.ResponseWriter, r *http.Request, ps
|
|||
writeResponseWithBody(w, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (s *UserMgmtServer) resendInvitationEmail(w http.ResponseWriter, r *http.Request, ps httprouter.Params, creds api.Creds) {
|
||||
id := ps.ByName("id")
|
||||
if id == "" {
|
||||
writeAPIError(w, http.StatusBadRequest, newAPIError(errorInvalidRequest, "id is required"))
|
||||
return
|
||||
}
|
||||
resendEmailInvitationReq := schema.ResendEmailInvitationRequest{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&resendEmailInvitationReq); err != nil {
|
||||
writeInvalidRequest(w, "cannot parse JSON body")
|
||||
return
|
||||
}
|
||||
|
||||
redirURL, err := url.Parse(resendEmailInvitationReq.RedirectURL)
|
||||
if err != nil {
|
||||
writeAPIError(w, http.StatusBadRequest,
|
||||
newAPIError(errorInvalidRequest, "redirectURL must be a valid URL"))
|
||||
return
|
||||
}
|
||||
|
||||
resendEmailInvitationResponse, err := s.api.ResendEmailInvitation(creds, id, *redirURL)
|
||||
if err != nil {
|
||||
s.writeError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
writeResponseWithBody(w, http.StatusOK, resendEmailInvitationResponse)
|
||||
}
|
||||
|
||||
func (s *UserMgmtServer) writeError(w http.ResponseWriter, err error) {
|
||||
log.Errorf("Error calling user management API: %v: ", err)
|
||||
if apiErr, ok := err.(api.Error); ok {
|
||||
|
|
|
@ -24,7 +24,8 @@ var (
|
|||
client.ErrorNotFound: ErrorInvalidClient,
|
||||
}
|
||||
|
||||
ErrorInvalidEmail = newError("invalid_email", "invalid email.", http.StatusBadRequest)
|
||||
ErrorInvalidEmail = newError("invalid_email", "invalid email.", http.StatusBadRequest)
|
||||
ErrorVerifiedEmail = newError("verified_email", "Email already verified.", http.StatusBadRequest)
|
||||
|
||||
ErrorInvalidClient = newError("invalid_client", "invalid email.", http.StatusBadRequest)
|
||||
|
||||
|
@ -188,6 +189,50 @@ func (u *UsersAPI) CreateUser(creds Creds, usr schema.User, redirURL url.URL) (s
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (u *UsersAPI) ResendEmailInvitation(creds Creds, userID string, redirURL url.URL) (schema.ResendEmailInvitationResponse, error) {
|
||||
log.Infof("userAPI: ResendEmailInvitation")
|
||||
if !u.Authorize(creds) {
|
||||
return schema.ResendEmailInvitationResponse{}, ErrorUnauthorized
|
||||
}
|
||||
|
||||
metadata, err := u.clientIdentityRepo.Metadata(creds.ClientID)
|
||||
if err != nil {
|
||||
return schema.ResendEmailInvitationResponse{}, mapError(err)
|
||||
}
|
||||
|
||||
validRedirURL, err := client.ValidRedirectURL(&redirURL, metadata.RedirectURIs)
|
||||
if err != nil {
|
||||
return schema.ResendEmailInvitationResponse{}, ErrorInvalidRedirectURL
|
||||
}
|
||||
|
||||
// Retrieve user to check if it's already created
|
||||
userUser, err := u.manager.Get(userID)
|
||||
if err != nil {
|
||||
return schema.ResendEmailInvitationResponse{}, mapError(err)
|
||||
}
|
||||
|
||||
// Check if email is verified
|
||||
if userUser.EmailVerified {
|
||||
return schema.ResendEmailInvitationResponse{}, ErrorVerifiedEmail
|
||||
}
|
||||
|
||||
url, err := u.emailer.SendInviteEmail(userUser.Email, validRedirURL, creds.ClientID)
|
||||
|
||||
// An email is sent only if we don't get a link and there's no error.
|
||||
emailSent := err == nil && url == nil
|
||||
|
||||
// If email is not sent a reset link will be generated
|
||||
var resetLink string
|
||||
if url != nil {
|
||||
resetLink = url.String()
|
||||
}
|
||||
|
||||
return schema.ResendEmailInvitationResponse{
|
||||
EmailSent: emailSent,
|
||||
ResetPasswordLink: resetLink,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (u *UsersAPI) ListUsers(creds Creds, maxResults int, nextPageToken string) ([]*schema.User, string, error) {
|
||||
log.Infof("userAPI: ListUsers")
|
||||
|
||||
|
|
|
@ -99,9 +99,10 @@ func makeTestFixtures() (*UsersAPI, *testEmailer) {
|
|||
},
|
||||
}, {
|
||||
User: user.User{
|
||||
ID: "ID-2",
|
||||
Email: "id2@example.com",
|
||||
CreatedAt: clock.Now(),
|
||||
ID: "ID-2",
|
||||
Email: "id2@example.com",
|
||||
EmailVerified: true,
|
||||
CreatedAt: clock.Now(),
|
||||
},
|
||||
}, {
|
||||
User: user.User{
|
||||
|
@ -463,3 +464,101 @@ func TestDisableUsers(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
func TestResendEmailInvitation(t *testing.T) {
|
||||
tests := []struct {
|
||||
creds Creds
|
||||
userID string
|
||||
email string
|
||||
redirURL url.URL
|
||||
cantEmail bool
|
||||
|
||||
wantResponse schema.ResendEmailInvitationResponse
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
creds: goodCreds,
|
||||
userID: "ID-1",
|
||||
email: "id1@example.com",
|
||||
redirURL: validRedirURL,
|
||||
|
||||
wantResponse: schema.ResendEmailInvitationResponse{
|
||||
EmailSent: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
creds: goodCreds,
|
||||
userID: "ID-1",
|
||||
email: "id1@example.com",
|
||||
redirURL: validRedirURL,
|
||||
cantEmail: true,
|
||||
|
||||
wantResponse: schema.ResendEmailInvitationResponse{
|
||||
EmailSent: false,
|
||||
ResetPasswordLink: resetPasswordURL.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
creds: badCreds,
|
||||
userID: "ID-1",
|
||||
email: "id1@example.com",
|
||||
redirURL: validRedirURL,
|
||||
|
||||
wantErr: ErrorUnauthorized,
|
||||
},
|
||||
{
|
||||
creds: goodCreds,
|
||||
userID: "ID-1",
|
||||
email: "id1@example.com",
|
||||
redirURL: url.URL{Host: "scammers.com"},
|
||||
|
||||
wantErr: ErrorInvalidRedirectURL,
|
||||
},
|
||||
{
|
||||
creds: goodCreds,
|
||||
userID: "ID-2",
|
||||
email: "id2@example.com",
|
||||
redirURL: validRedirURL,
|
||||
|
||||
wantErr: ErrorVerifiedEmail,
|
||||
},
|
||||
{
|
||||
creds: goodCreds,
|
||||
userID: "non-existent",
|
||||
email: "non-existent@example.com",
|
||||
redirURL: validRedirURL,
|
||||
|
||||
wantErr: ErrorResourceNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
api, emailer := makeTestFixtures()
|
||||
emailer.cantEmail = tt.cantEmail
|
||||
|
||||
response, err := api.ResendEmailInvitation(tt.creds, tt.userID, tt.redirURL)
|
||||
if tt.wantErr != nil {
|
||||
if err != tt.wantErr {
|
||||
t.Errorf("case %d: want=%q, got=%q", i, tt.wantErr, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("case %d: want nil err, got: %q ", i, err)
|
||||
}
|
||||
|
||||
if diff := pretty.Compare(tt.wantResponse, response); diff != "" {
|
||||
t.Errorf("case %d: Compare(want, got) = %v", i, diff)
|
||||
}
|
||||
|
||||
wantEmailer := testEmailer{
|
||||
cantEmail: tt.cantEmail,
|
||||
lastEmail: tt.email,
|
||||
lastClientID: tt.creds.ClientID,
|
||||
lastRedirectURL: tt.redirURL,
|
||||
lastWasInvite: true,
|
||||
}
|
||||
if diff := pretty.Compare(wantEmailer, emailer); diff != "" {
|
||||
t.Errorf("case %d: Compare(want, got) = %v", i, diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue