add support for resend an invite email
This change solves the User's API problem when you want to create an user that its email hasn't been verified yet but it exist. At now, you can resend invitation email using endpoint /users/{id}/resend-invitation Fixes #184
This commit is contained in:
parent
f51125f555
commit
8156870862
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