From e6ddd6b031531d7c4749c9aec595176690c88955 Mon Sep 17 00:00:00 2001 From: Piotr Zduniak Date: Wed, 14 Jan 2015 00:50:38 +0100 Subject: [PATCH 1/6] Registration flow change --- models/token.go | 2 - routes/accounts.go | 372 +++++++++++++++++++++++++-------------------- 2 files changed, 203 insertions(+), 171 deletions(-) diff --git a/models/token.go b/models/token.go index c1c93f3..90c415a 100644 --- a/models/token.go +++ b/models/token.go @@ -7,8 +7,6 @@ type Token struct { // Type describes the token's purpose: auth, invite, confirm, upgrade. Type string `json:"type" gorethink:"type"` - - Email string `json:"email,omitempty" gorethink:"email"` } // MakeToken creates a generic token. diff --git a/routes/accounts.go b/routes/accounts.go index 1c281ec..8a9fd3e 100644 --- a/routes/accounts.go +++ b/routes/accounts.go @@ -28,10 +28,10 @@ func AccountsList(w http.ResponseWriter, r *http.Request) { // AccountsCreateRequest contains the input for the AccountsCreate endpoint. type AccountsCreateRequest struct { - Token string `json:"token,omitempty" schema:"token"` - Username string `json:"username,omitempty" schema:"username"` - Password string `json:"password,omitempty" schema:"password"` - AltEmail string `json:"alt_email,omitempty" schema:"alt_email"` + Username string `json:"username,omitempty" schema:"username"` + Password string `json:"password,omitempty" schema:"password"` + AltEmail string `json:"alt_email,omitempty" schema:"alt_email"` + InviteCode string `json:"InviteCode,omitempty" schema:"InviteCode"` } // AccountsCreateResponse contains the output of the AccountsCreate request. @@ -58,20 +58,20 @@ func AccountsCreate(w http.ResponseWriter, r *http.Request) { return } - // Detect the request type - // 1) username + token + password - invite - // 2) username + password + alt_email - register with confirmation - // 3) alt_email only - register for beta (add to queue) - // 4) alt_email + username - register for beta with username reservation + // TODO: Sanitize the username + // TODO: Hash the password if it's not hashed already + + // Accounts flow: + // 1) POST /accounts {username, alt_email} => status = registered + // 2) POST /accounts {username, invite_code} => checks invite_code validity + // 3) POST /accounts {username, invite_code, password} => status = setup requestType := "unknown" - if input.AltEmail == "" && input.Username != "" && input.Password != "" && input.Token != "" { - requestType = "invited" - } else if input.AltEmail != "" && input.Username != "" && input.Password != "" && input.Token == "" { - requestType = "classic" - } else if input.AltEmail != "" && input.Username == "" && input.Password == "" && input.Token == "" { - requestType = "queue/classic" - } else if input.AltEmail != "" && input.Username != "" && input.Password == "" && input.Token == "" { - requestType = "queue/reserve" + if input.Username != "" && input.Password == "" && input.AltEmail != "" && input.InviteCode == "" { + requestType = "register" + } else if input.Username != "" && input.Password == "" && input.AltEmail == "" && input.InviteCode != "" { + requestType = "verify" + } else if input.Username != "" && input.Password != "" && input.AltEmail == "" && input.InviteCode != "" { + requestType = "setup" } // "unknown" requests are empty and invalid @@ -83,117 +83,159 @@ func AccountsCreate(w http.ResponseWriter, r *http.Request) { return } - if input.Username != "" { - if used, err := env.Reservations.IsUsernameUsed(input.Username); err != nil || used { + if requestType == "register" { + // Ensure that the username is not used + if used, err := env.Accounts.IsUsernameUsed(input.Username); err != nil || used { if err != nil { env.Log.WithFields(logrus.Fields{ "error": err.Error(), - }).Error("Unable to lookup reservations for usernames") + }).Error("Unable to lookup registered accounts for usernames") } utils.JSONResponse(w, 400, &AccountsCreateResponse{ Success: false, - Message: "Username already reserved", + Message: "Username already used", }) return } - if used, err := env.Accounts.IsUsernameUsed(input.Username); err != nil || used { + // Also check that the email is unique + if used, err := env.Accounts.IsEmailUsed(input.AltEmail); err != nil || used { if err != nil { env.Log.WithFields(logrus.Fields{ "error": err.Error(), - }).Error("Unable to lookup registered accounts for usernames") + }).Error("Unable to lookup registered accounts for emails") } utils.JSONResponse(w, 400, &AccountsCreateResponse{ Success: false, - Message: "Username already used", + Message: "Email already used", }) return } - } - // Adding to [beta] queue - if requestType[:5] == "queue" { - if requestType[6:] == "reserve" { - // Is username reservation enabled? - if !env.Config.UsernameReservation { - utils.JSONResponse(w, 403, &AccountsCreateResponse{ - Success: false, - Message: "Username reservation is disabled", - }) - return - } + // Both username and email are filled, so we can create a new account. + account := &models.Account{ + Resource: models.MakeResource("", input.Username), + Type: "beta", // Is this the proper value? + AltEmail: input.AltEmail, + Status: "registered", + } + + // Try to save it in the database + if err := env.Accounts.Insert(account); err != nil { + utils.JSONResponse(w, 500, &AccountsCreateResponse{ + Success: false, + Message: "Internal server error - AC/CR/02", + }) + + env.Log.WithFields(logrus.Fields{ + "error": err.Error(), + }).Error("Could not insert an user into the database") + return } - // Ensure that the email is not already used to reserve/register - if used, err := env.Reservations.IsEmailUsed(input.AltEmail); err != nil || used { + // TODO: Send emails here. Depends on @andreis work. + + // Return information about the account + utils.JSONResponse(w, 201, &AccountsCreateResponse{ + Success: true, + Message: "Your account has been added to the beta queue", + Account: account, + }) + return + } else if requestType == "verify" { + // We're pretty much checking whether an invitation code can be used by the user + + // Fetch the user from database + account, err := env.Accounts.FindAccountByName(input.Username) + if err != nil { + env.Log.WithFields(logrus.Fields{ + "error": err.Error(), + "username": input.Username, + }).Warn("User not found in the database") + utils.JSONResponse(w, 400, &AccountsCreateResponse{ Success: false, - Message: "Email already used for a reservation", + Message: "Invalid username", }) return } - if used, err := env.Accounts.IsEmailUsed(input.AltEmail); err != nil || used { + // Fetch the token from the database + token, err := env.Tokens.GetToken(input.InviteCode) + if err != nil { + env.Log.WithFields(logrus.Fields{ + "error": err.Error(), + }).Warn("Unable to fetch a registration token from the database") + utils.JSONResponse(w, 400, &AccountsCreateResponse{ Success: false, - Message: "Email already used for a reservation", + Message: "Invalid invitation code", }) return } - // Prepare data to insert - reservation := &models.Reservation{ - Email: input.AltEmail, - Resource: models.MakeResource("", input.Username), + // Ensure that the invite code was given to this particular user. + if token.Owner != account.ID { + env.Log.WithFields(logrus.Fields{ + "error": err.Error(), + "user_id": account.ID, + "owner": token.Owner, + }).Warn("Not owned invitation code used by an user") + + utils.JSONResponse(w, 400, &AccountsCreateResponse{ + Success: false, + Message: "Invalid invitation code", + }) + return } - err := env.Reservations.Insert(reservation) - if err != nil { - utils.JSONResponse(w, 500, &AccountsCreateResponse{ + // Ensure that the token's type is valid + if token.Type != "invite" { + utils.JSONResponse(w, 400, &AccountsCreateResponse{ Success: false, - Message: "Internal error while reserving the account", + Message: "Invalid invitation code", }) return } - utils.JSONResponse(w, 201, &AccountsCreateResponse{ - Success: true, - Message: "Reserved an account", - }) - return - } + // Check if it's expired + if token.Expired() { + utils.JSONResponse(w, 400, &AccountsCreateResponse{ + Success: false, + Message: "Expired invitation code", + }) + return + } - // Check if classic registration is enabled - if requestType == "classic" && !env.Config.ClassicRegistration { - utils.JSONResponse(w, 403, &AccountsCreateResponse{ - Success: false, - Message: "Classic registration is disabled", + // Everything is fine, return it. + utils.JSONResponse(w, 200, &AccountsCreateResponse{ + Success: true, + Message: "Valid token was used", }) return - } + } else if requestType == "setup" { + // User is setting the password in the setup wizard. This should be one of the first steps, + // as it's required for him to acquire an authentication token to configure their account. - // Check for generic passwords - if input.Password != "" && !utils.IsPasswordSecure(input.Password) { - utils.JSONResponse(w, 403, &AccountsCreateResponse{ - Success: false, - Message: "Weak password", - }) - return - } + // Fetch the user from database + account, err := env.Accounts.FindAccountByName(input.Username) + if err != nil { + env.Log.WithFields(logrus.Fields{ + "error": err.Error(), + "username": input.Username, + }).Warn("User not found in the database") - // Both username and password are filled, so we can create a new account. - account := &models.Account{ - Resource: models.MakeResource("", input.Username), - Type: "beta", - AltEmail: input.AltEmail, - } + utils.JSONResponse(w, 400, &AccountsCreateResponse{ + Success: false, + Message: "Invalid username", + }) + return + } - // Check "invited" for token validity - if requestType == "invited" { // Fetch the token from the database - token, err := env.Tokens.GetToken(input.Token) + token, err := env.Tokens.GetToken(input.InviteCode) if err != nil { env.Log.WithFields(logrus.Fields{ "error": err.Error(), @@ -201,7 +243,22 @@ func AccountsCreate(w http.ResponseWriter, r *http.Request) { utils.JSONResponse(w, 400, &AccountsCreateResponse{ Success: false, - Message: "Invalid invitation token", + Message: "Invalid invitation code", + }) + return + } + + // Ensure that the invite code was given to this particular user. + if token.Owner != account.ID { + env.Log.WithFields(logrus.Fields{ + "error": err.Error(), + "user_id": account.ID, + "owner": token.Owner, + }).Warn("Not owned invitation code used by an user") + + utils.JSONResponse(w, 400, &AccountsCreateResponse{ + Success: false, + Message: "Invalid invitation code", }) return } @@ -210,7 +267,7 @@ func AccountsCreate(w http.ResponseWriter, r *http.Request) { if token.Type != "invite" { utils.JSONResponse(w, 400, &AccountsCreateResponse{ Success: false, - Message: "Invalid invitation token", + Message: "Invalid invitation code", }) return } @@ -219,113 +276,90 @@ func AccountsCreate(w http.ResponseWriter, r *http.Request) { if token.Expired() { utils.JSONResponse(w, 400, &AccountsCreateResponse{ Success: false, - Message: "Expired invitation token", + Message: "Expired invitation code", }) return } - account.AltEmail = token.Email - } - - // TODO: sanitize user name (i.e. remove caps, periods) + // Our token is fine, next part: password. - // Set the password - err = account.SetPassword(input.Password) - if err != nil { - utils.JSONResponse(w, 500, &AccountsCreateResponse{ - Success: false, - Message: "Internal server error - AC/CR/01", - }) - - env.Log.WithFields(logrus.Fields{ - "error": err.Error(), - }).Error("Unable to hash the password") - return - } - - // User won't be able to log in until the account gets verified - if requestType == "classic" { - account.Status = "unverified" - } + // Ensure that user has chosen a secure password (check against 10k most used) + if !utils.IsPasswordSecure(input.Password) { + utils.JSONResponse(w, 403, &AccountsCreateResponse{ + Success: false, + Message: "Weak password", + }) + return + } - // Set the status to invited, because of stats - if requestType == "invited" { - account.Status = "invited" - } + // We can't really make more checks on the password, user could as well send us a hash + // of a simple password, but we assume that no developer is that stupid (actually, + // considering how many people upload their private keys and AWS credentials, I'm starting + // to doubt the competence of some so-called "web deyvelopayrs") - // Try to save it in the database - if err := env.Accounts.Insert(account); err != nil { - utils.JSONResponse(w, 500, &AccountsCreateResponse{ - Success: false, - Message: "Internal server error - AC/CR/02", - }) + // Set the password + err = account.SetPassword(input.Password) + if err != nil { + utils.JSONResponse(w, 500, &AccountsCreateResponse{ + Success: false, + Message: "Internal server error - AC/CR/01", + }) - env.Log.WithFields(logrus.Fields{ - "error": err.Error(), - }).Error("Could not insert an user into the database") - return - } + env.Log.WithFields(logrus.Fields{ + "error": err.Error(), + }).Error("Unable to hash the password") + return + } - // Create labels - err = env.Labels.Insert([]*models.Label{ - &models.Label{ - Resource: models.MakeResource(account.ID, "Inbox"), - Builtin: true, - }, - &models.Label{ - Resource: models.MakeResource(account.ID, "Sent"), - Builtin: true, - }, - &models.Label{ - Resource: models.MakeResource(account.ID, "Trash"), - Builtin: true, - }, - &models.Label{ - Resource: models.MakeResource(account.ID, "Spam"), - Builtin: true, - }, - &models.Label{ - Resource: models.MakeResource(account.ID, "Starred"), - Builtin: true, - }, - }) - if err != nil { - utils.JSONResponse(w, 500, &AccountsCreateResponse{ - Success: false, - Message: "Internal server error - AC/CR/03", + account.Status = "setup" + + // Create labels + err = env.Labels.Insert([]*models.Label{ + &models.Label{ + Resource: models.MakeResource(account.ID, "Inbox"), + Builtin: true, + }, + &models.Label{ + Resource: models.MakeResource(account.ID, "Sent"), + Builtin: true, + }, + &models.Label{ + Resource: models.MakeResource(account.ID, "Trash"), + Builtin: true, + }, + &models.Label{ + Resource: models.MakeResource(account.ID, "Spam"), + Builtin: true, + }, + &models.Label{ + Resource: models.MakeResource(account.ID, "Starred"), + Builtin: true, + }, }) + if err != nil { + utils.JSONResponse(w, 500, &AccountsCreateResponse{ + Success: false, + Message: "Internal server error - AC/CR/03", + }) - env.Log.WithFields(logrus.Fields{ - "error": err.Error(), - }).Error("Could not insert labels into the database") - return - } - - // Send the email if classic and return a response - if requestType == "classic" { - // TODO: Send emails - - utils.JSONResponse(w, 201, &AccountsCreateResponse{ - Success: true, - Message: "A new account was successfully created, you should receive a confirmation email soon™.", - Account: account, - }) - return - } + env.Log.WithFields(logrus.Fields{ + "error": err.Error(), + }).Error("Could not insert labels into the database") + return + } - // Remove the token and return a response - if requestType == "invited" { - err := env.Tokens.DeleteID(input.Token) + // Remove the token and return a response + err = env.Tokens.DeleteID(input.InviteCode) if err != nil { env.Log.WithFields(logrus.Fields{ "error": err.Error(), - "id": input.Token, - }).Error("Could not remove token from database") + "id": input.InviteCode, + }).Error("Could not remove the token from database") } - utils.JSONResponse(w, 201, &AccountsCreateResponse{ + utils.JSONResponse(w, 200, &AccountsCreateResponse{ Success: true, - Message: "A new account was successfully created", + Message: "Your account has been configured successfully", Account: account, }) return From 059b50f3997ac88cdb9d31ddc9de45f644de5a86 Mon Sep 17 00:00:00 2001 From: Piotr Zduniak Date: Wed, 14 Jan 2015 01:00:49 +0100 Subject: [PATCH 2/6] don't let in users whose status is registered --- routes/tokens.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/routes/tokens.go b/routes/tokens.go index 3b6e19c..1a8c0af 100644 --- a/routes/tokens.go +++ b/routes/tokens.go @@ -107,6 +107,15 @@ func TokensCreate(w http.ResponseWriter, r *http.Request) { return } + // "registered" accounts can't log in + if user.Status == "registered" { + utils.JSONResponse(w, 403, &TokensCreateResponse{ + Success: false, + Message: "Your account is not confirmed", + }) + return + } + // Verify the password valid, updated, err := user.VerifyPassword(input.Password) if err != nil || !valid { From 0dbcdc8347e0cc96f5fb1d6c93df58d9c4217916 Mon Sep 17 00:00:00 2001 From: Piotr Zduniak Date: Wed, 14 Jan 2015 14:37:27 +0100 Subject: [PATCH 3/6] Gradually fixing tests --- routes/accounts_test.go | 99 ++++++++++--------- .../{contacts_test.go => contacts_test.go_} | 3 +- routes/{emails_test.go => emails_test.go_} | 0 routes/init_test.go | 1 - routes/{keys_test.go => keys_test.go_} | 0 ...middleware_test.go => middleware_test.go_} | 0 routes/{tokens_test.go => tokens_test.go_} | 7 +- 7 files changed, 58 insertions(+), 52 deletions(-) rename routes/{contacts_test.go => contacts_test.go_} (99%) rename routes/{emails_test.go => emails_test.go_} (100%) rename routes/{keys_test.go => keys_test.go_} (100%) rename routes/{middleware_test.go => middleware_test.go_} (100%) rename routes/{tokens_test.go => tokens_test.go_} (98%) diff --git a/routes/accounts_test.go b/routes/accounts_test.go index de7637d..fda5e14 100644 --- a/routes/accounts_test.go +++ b/routes/accounts_test.go @@ -2,16 +2,23 @@ package routes_test import ( "testing" - "time" + //"time" "github.com/franela/goreq" "github.com/stretchr/testify/require" + "golang.org/x/crypto/sha3" - "github.com/lavab/api/env" - "github.com/lavab/api/models" + //"github.com/lavab/api/env" + //"github.com/lavab/api/models" "github.com/lavab/api/routes" ) +var ( + accountUsername string + accountPassword string + accountID string +) + func TestAccountsCreateInvalid(t *testing.T) { result, err := goreq.Request{ Method: "POST", @@ -46,86 +53,87 @@ func TestAccountsCreateUnknown(t *testing.T) { require.Equal(t, "Invalid request", response.Message) } -func TestAccountsCreateInvited(t *testing.T) { +func TestAccountsCreateRegister(t *testing.T) { const ( username = "jeremy" password = "potato" + email = "jeremy@potato.org" ) + // Prepare account information + accountUsername = username + passwordHash := sha3.Sum256([]byte(password)) + accountPassword = string(passwordHash[:]) + // Prepare a token - inviteToken := models.Token{ + /*inviteToken := models.Token{ Resource: models.MakeResource("", "test invite token"), Type: "invite", } inviteToken.ExpireSoon() err := env.Tokens.Insert(inviteToken) - require.Nil(t, err) + require.Nil(t, err)*/ // POST /accounts - invited - result1, err := goreq.Request{ + result, err := goreq.Request{ Method: "POST", Uri: server.URL + "/accounts", ContentType: "application/json", Body: routes.AccountsCreateRequest{ Username: username, - Password: password, - Token: inviteToken.ID, + AltEmail: email, }, }.Do() require.Nil(t, err) // Unmarshal the response - var response1 routes.AccountsCreateResponse - err = result1.Body.FromJsonTo(&response1) + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) require.Nil(t, err) // Check the result's contents - require.Equal(t, "A new account was successfully created", response1.Message) - require.True(t, response1.Success) - require.NotEmpty(t, response1.Account.ID) + require.Equal(t, "Your account has been added to the beta queue", response.Message) + require.True(t, response.Success) + require.NotEmpty(t, response.Account.ID) + + accountID = response.Account.ID +} - accountID = response1.Account.ID +func TestAccountsCreateInvitedExistingUsername(t *testing.T) { + const ( + username = "jeremy" + email = "jeremy@potato.org" + ) - // POST /accounts - invited with wrong token - result2, err := goreq.Request{ + // POST /accounts - invited + result, err := goreq.Request{ Method: "POST", Uri: server.URL + "/accounts", ContentType: "application/json", Body: routes.AccountsCreateRequest{ - Username: username + "2", - Password: password, - Token: "asdasdasd", + Username: username, + AltEmail: email, }, }.Do() require.Nil(t, err) // Unmarshal the response - var response2 routes.AccountsCreateResponse - err = result2.Body.FromJsonTo(&response2) + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) require.Nil(t, err) // Check the result's contents - require.False(t, response2.Success) - require.Equal(t, "Invalid invitation token", response2.Message) + require.False(t, response.Success) + require.Equal(t, "Username already used", response.Message) } -func TestAccountsCreateInvitedExisting(t *testing.T) { +func TestAccountsCreateInvitedExistingEmail(t *testing.T) { const ( - username = "jeremy" - password = "potato" + username = "jeremy2" + email = "jeremy@potato.org" ) - // Prepare a token - inviteToken := models.Token{ - Resource: models.MakeResource("", "test invite token"), - Type: "invite", - } - inviteToken.ExpireSoon() - - err := env.Tokens.Insert(inviteToken) - require.Nil(t, err) - // POST /accounts - invited result, err := goreq.Request{ Method: "POST", @@ -133,8 +141,7 @@ func TestAccountsCreateInvitedExisting(t *testing.T) { ContentType: "application/json", Body: routes.AccountsCreateRequest{ Username: username, - Password: password, - Token: inviteToken.ID, + AltEmail: email, }, }.Do() require.Nil(t, err) @@ -146,9 +153,10 @@ func TestAccountsCreateInvitedExisting(t *testing.T) { // Check the result's contents require.False(t, response.Success) - require.Equal(t, "Username already used", response.Message) + require.Equal(t, "Email already used", response.Message) } +/* func TestAccountsCreateInvitedWeakPassword(t *testing.T) { const ( username = "jeremylicious" @@ -463,20 +471,16 @@ func TestAccountsCreateQueueClassicReservedEmail(t *testing.T) { require.False(t, response.Success) } + func TestAccountsPrepareToken(t *testing.T) { - // log in as mr jeremy potato - const ( - username = "jeremy" - password = "potato" - ) // POST /accounts - classic request, err := goreq.Request{ Method: "POST", Uri: server.URL + "/tokens", ContentType: "application/json", Body: routes.TokensCreateRequest{ - Username: username, - Password: password, + Username: accountUsername, + Password: accountPassword, Type: "auth", }, }.Do() @@ -756,3 +760,4 @@ func TestAccountsDelete(t *testing.T) { require.Equal(t, "Your account has been successfully deleted", response.Message) require.True(t, response.Success) } +*/ diff --git a/routes/contacts_test.go b/routes/contacts_test.go_ similarity index 99% rename from routes/contacts_test.go rename to routes/contacts_test.go_ index 6468423..4f87913 100644 --- a/routes/contacts_test.go +++ b/routes/contacts_test.go_ @@ -16,6 +16,7 @@ var ( notOwnedContactID string ) +/* func TestContactsPrepareAccount(t *testing.T) { const ( username = "jeremy-contacts" @@ -75,7 +76,7 @@ func TestContactsPrepareAccount(t *testing.T) { authToken = response2.Token.ID } - +*/ func TestContactsCreate(t *testing.T) { request := goreq.Request{ Method: "POST", diff --git a/routes/emails_test.go b/routes/emails_test.go_ similarity index 100% rename from routes/emails_test.go rename to routes/emails_test.go_ diff --git a/routes/init_test.go b/routes/init_test.go index fb0e4e7..d5619f7 100644 --- a/routes/init_test.go +++ b/routes/init_test.go @@ -13,7 +13,6 @@ import ( var ( server *httptest.Server - accountID string authToken string ) diff --git a/routes/keys_test.go b/routes/keys_test.go_ similarity index 100% rename from routes/keys_test.go rename to routes/keys_test.go_ diff --git a/routes/middleware_test.go b/routes/middleware_test.go_ similarity index 100% rename from routes/middleware_test.go rename to routes/middleware_test.go_ diff --git a/routes/tokens_test.go b/routes/tokens_test.go_ similarity index 98% rename from routes/tokens_test.go rename to routes/tokens_test.go_ index 487de7e..64f214f 100644 --- a/routes/tokens_test.go +++ b/routes/tokens_test.go_ @@ -7,11 +7,12 @@ import ( "github.com/franela/goreq" "github.com/stretchr/testify/require" - "github.com/lavab/api/env" - "github.com/lavab/api/models" + //"github.com/lavab/api/env" + //"github.com/lavab/api/models" "github.com/lavab/api/routes" ) +/* func TestTokensPrepareAccount(t *testing.T) { const ( username = "jeremy" @@ -53,7 +54,7 @@ func TestTokensPrepareAccount(t *testing.T) { accountID = response1.Account.ID } - +*/ func TestTokensCreate(t *testing.T) { // log in as mr jeremy potato const ( From 261e29fea84ad67e7faa9b54209580846c663d57 Mon Sep 17 00:00:00 2001 From: Piotr Zduniak Date: Wed, 14 Jan 2015 21:48:56 +0100 Subject: [PATCH 4/6] Almost all accounts tests --- routes/accounts.go | 25 +++- routes/accounts_test.go | 289 ++++++++++++++++++++++++---------------- setup/setup.go | 6 + 3 files changed, 197 insertions(+), 123 deletions(-) diff --git a/routes/accounts.go b/routes/accounts.go index 8a9fd3e..1ada6a8 100644 --- a/routes/accounts.go +++ b/routes/accounts.go @@ -179,7 +179,6 @@ func AccountsCreate(w http.ResponseWriter, r *http.Request) { // Ensure that the invite code was given to this particular user. if token.Owner != account.ID { env.Log.WithFields(logrus.Fields{ - "error": err.Error(), "user_id": account.ID, "owner": token.Owner, }).Warn("Not owned invitation code used by an user") @@ -192,7 +191,7 @@ func AccountsCreate(w http.ResponseWriter, r *http.Request) { } // Ensure that the token's type is valid - if token.Type != "invite" { + if token.Type != "verify" { utils.JSONResponse(w, 400, &AccountsCreateResponse{ Success: false, Message: "Invalid invitation code", @@ -212,7 +211,7 @@ func AccountsCreate(w http.ResponseWriter, r *http.Request) { // Everything is fine, return it. utils.JSONResponse(w, 200, &AccountsCreateResponse{ Success: true, - Message: "Valid token was used", + Message: "Valid token was provided", }) return } else if requestType == "setup" { @@ -251,7 +250,6 @@ func AccountsCreate(w http.ResponseWriter, r *http.Request) { // Ensure that the invite code was given to this particular user. if token.Owner != account.ID { env.Log.WithFields(logrus.Fields{ - "error": err.Error(), "user_id": account.ID, "owner": token.Owner, }).Warn("Not owned invitation code used by an user") @@ -264,7 +262,7 @@ func AccountsCreate(w http.ResponseWriter, r *http.Request) { } // Ensure that the token's type is valid - if token.Type != "invite" { + if token.Type != "verify" { utils.JSONResponse(w, 400, &AccountsCreateResponse{ Success: false, Message: "Invalid invitation code", @@ -348,6 +346,21 @@ func AccountsCreate(w http.ResponseWriter, r *http.Request) { return } + // Update the account + err = env.Accounts.UpdateID(account.ID, account) + if err != nil { + env.Log.WithFields(logrus.Fields{ + "error": err.Error(), + "id": account.ID, + }).Error("Unable to update an account") + + utils.JSONResponse(w, 500, &AccountsCreateResponse{ + Success: false, + Message: "Unable to update the account", + }) + return + } + // Remove the token and return a response err = env.Tokens.DeleteID(input.InviteCode) if err != nil { @@ -359,7 +372,7 @@ func AccountsCreate(w http.ResponseWriter, r *http.Request) { utils.JSONResponse(w, 200, &AccountsCreateResponse{ Success: true, - Message: "Your account has been configured successfully", + Message: "Your account has been initialized successfully", Account: account, }) return diff --git a/routes/accounts_test.go b/routes/accounts_test.go index fda5e14..4648093 100644 --- a/routes/accounts_test.go +++ b/routes/accounts_test.go @@ -2,21 +2,22 @@ package routes_test import ( "testing" - //"time" + "time" "github.com/franela/goreq" "github.com/stretchr/testify/require" "golang.org/x/crypto/sha3" - //"github.com/lavab/api/env" - //"github.com/lavab/api/models" + "github.com/lavab/api/env" + "github.com/lavab/api/models" "github.com/lavab/api/routes" ) var ( - accountUsername string - accountPassword string - accountID string + accountUsername string + accountPassword string + accountID string + verificationTokenID string ) func TestAccountsCreateInvalid(t *testing.T) { @@ -156,32 +157,27 @@ func TestAccountsCreateInvitedExistingEmail(t *testing.T) { require.Equal(t, "Email already used", response.Message) } -/* -func TestAccountsCreateInvitedWeakPassword(t *testing.T) { - const ( - username = "jeremylicious" - password = "c0067d4af4e87f00dbac63b6156828237059172d1bbeac67427345d6a9fda484" - ) - +func TestAccountsCreateVerify(t *testing.T) { // Prepare a token - inviteToken := models.Token{ - Resource: models.MakeResource("", "test invite token"), - Type: "invite", + verificationToken := models.Token{ + Resource: models.MakeResource(accountID, "test verification token"), + Type: "verify", } - inviteToken.ExpireSoon() + verificationToken.ExpireSoon() - err := env.Tokens.Insert(inviteToken) + err := env.Tokens.Insert(verificationToken) require.Nil(t, err) + verificationTokenID = verificationToken.ID + // POST /accounts - invited result, err := goreq.Request{ Method: "POST", Uri: server.URL + "/accounts", ContentType: "application/json", Body: routes.AccountsCreateRequest{ - Username: username, - Password: password, - Token: inviteToken.ID, + Username: accountUsername, + InviteCode: verificationTokenID, }, }.Do() require.Nil(t, err) @@ -192,35 +188,42 @@ func TestAccountsCreateInvitedWeakPassword(t *testing.T) { require.Nil(t, err) // Check the result's contents - require.False(t, response.Success) - require.Equal(t, "Weak password", response.Message) + require.Equal(t, "Valid token was provided", response.Message) + require.True(t, response.Success) } -func TestAccountsCreateInvitedExpired(t *testing.T) { - const ( - username = "jeremy2" - password = "potato2" - ) - - // Prepare a token - inviteToken := models.Token{ - Resource: models.MakeResource("", "test invite token"), - Type: "invite", - } - inviteToken.ExpiryDate = time.Now().Truncate(time.Hour) +func TestAccountsCreateVerifyInvalidUsername(t *testing.T) { + // POST /accounts - invited + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: "topkek", + InviteCode: verificationTokenID, + }, + }.Do() + require.Nil(t, err) - err := env.Tokens.Insert(inviteToken) + // Unmarshal the response + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) require.Nil(t, err) + // Check the result's contents + require.Equal(t, "Invalid username", response.Message) + require.False(t, response.Success) +} + +func TestAccountsCreateVerifyInvalidCode(t *testing.T) { // POST /accounts - invited result, err := goreq.Request{ Method: "POST", Uri: server.URL + "/accounts", ContentType: "application/json", Body: routes.AccountsCreateRequest{ - Username: username, - Password: password, - Token: inviteToken.ID, + Username: accountUsername, + InviteCode: "random shtuff", }, }.Do() require.Nil(t, err) @@ -231,24 +234,19 @@ func TestAccountsCreateInvitedExpired(t *testing.T) { require.Nil(t, err) // Check the result's contents + require.Equal(t, "Invalid invitation code", response.Message) require.False(t, response.Success) - require.Equal(t, "Expired invitation token", response.Message) } -func TestAccountsCreateInvitedWrongType(t *testing.T) { - const ( - username = "jeremy2" - password = "potato2" - ) - +func TestAccountsCreateVerifyNotOwnedCode(t *testing.T) { // Prepare a token - inviteToken := models.Token{ - Resource: models.MakeResource("", "test not invite token"), - Type: "not invite", + verificationToken := models.Token{ + Resource: models.MakeResource("top kek", "test verification token"), + Type: "verify", } - inviteToken.ExpiryDate = time.Now().Truncate(time.Hour) + verificationToken.ExpireSoon() - err := env.Tokens.Insert(inviteToken) + err := env.Tokens.Insert(verificationToken) require.Nil(t, err) // POST /accounts - invited @@ -257,9 +255,8 @@ func TestAccountsCreateInvitedWrongType(t *testing.T) { Uri: server.URL + "/accounts", ContentType: "application/json", Body: routes.AccountsCreateRequest{ - Username: username, - Password: password, - Token: inviteToken.ID, + Username: accountUsername, + InviteCode: verificationToken.ID, }, }.Do() require.Nil(t, err) @@ -270,81 +267,106 @@ func TestAccountsCreateInvitedWrongType(t *testing.T) { require.Nil(t, err) // Check the result's contents + require.Equal(t, "Invalid invitation code", response.Message) require.False(t, response.Success) - require.Equal(t, "Invalid invitation token", response.Message) } -func TestAccountsCreateClassic(t *testing.T) { - const ( - username = "jeremy" - password = "potato" - ) +func TestAccountsCreateVerifyNotVerify(t *testing.T) { + // Prepare a token + verificationToken := models.Token{ + Resource: models.MakeResource(accountID, "test verification token"), + Type: "notverify", + } + verificationToken.ExpireSoon() - // POST /accounts - classic - createClassicResult, err := goreq.Request{ + err := env.Tokens.Insert(verificationToken) + require.Nil(t, err) + + // POST /accounts - invited + result, err := goreq.Request{ Method: "POST", Uri: server.URL + "/accounts", ContentType: "application/json", Body: routes.AccountsCreateRequest{ - Username: username + "classic", - Password: password, - AltEmail: "something@example.com", + Username: accountUsername, + InviteCode: verificationToken.ID, }, }.Do() require.Nil(t, err) // Unmarshal the response - var createClassicResponse routes.AccountsCreateResponse - err = createClassicResult.Body.FromJsonTo(&createClassicResponse) + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) require.Nil(t, err) // Check the result's contents - require.True(t, createClassicResponse.Success) - require.Equal(t, "A new account was successfully created, you should receive a confirmation email soon™.", createClassicResponse.Message) - require.NotEmpty(t, createClassicResponse.Account.ID) + require.Equal(t, "Invalid invitation code", response.Message) + require.False(t, response.Success) } -func TestAccountsCreateClassicDisabled(t *testing.T) { - const ( - username = "jeremy_was_invited" - password = "potato" - ) +func TestAccountsCreateVerifyExpired(t *testing.T) { + // Prepare a token + verificationToken := models.Token{ + Resource: models.MakeResource(accountID, "test verification token"), + Type: "verify", + } + verificationToken.ExpiryDate = time.Now().Truncate(time.Hour * 24) - env.Config.ClassicRegistration = false + err := env.Tokens.Insert(verificationToken) + require.Nil(t, err) - // POST /accounts - classic - createClassicResult, err := goreq.Request{ + // POST /accounts - invited + result, err := goreq.Request{ Method: "POST", Uri: server.URL + "/accounts", ContentType: "application/json", Body: routes.AccountsCreateRequest{ - Username: username + "classic", - Password: password, - AltEmail: "something@example.com", + Username: accountUsername, + InviteCode: verificationToken.ID, }, }.Do() require.Nil(t, err) // Unmarshal the response - var createClassicResponse routes.AccountsCreateResponse - err = createClassicResult.Body.FromJsonTo(&createClassicResponse) + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) require.Nil(t, err) // Check the result's contents - require.False(t, createClassicResponse.Success) - require.Equal(t, "Classic registration is disabled", createClassicResponse.Message) + require.Equal(t, "Expired invitation code", response.Message) + require.False(t, response.Success) +} - env.Config.ClassicRegistration = true +func TestAccountsCreateSetupWeakPassword(t *testing.T) { + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: accountUsername, + InviteCode: verificationTokenID, + Password: "c0067d4af4e87f00dbac63b6156828237059172d1bbeac67427345d6a9fda484", + }, + }.Do() + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + require.Nil(t, err) + + // Check the result's contents + require.Equal(t, "Weak password", response.Message) + require.False(t, response.Success) } -func TestAccountsCreateQueueReservation(t *testing.T) { +func TestAccountsCreateSetup(t *testing.T) { result, err := goreq.Request{ Method: "POST", Uri: server.URL + "/accounts", ContentType: "application/json", Body: routes.AccountsCreateRequest{ - AltEmail: "reserved@example.com", - Username: "reserved", + Username: accountUsername, + InviteCode: verificationTokenID, + Password: accountPassword, }, }.Do() require.Nil(t, err) @@ -355,18 +377,19 @@ func TestAccountsCreateQueueReservation(t *testing.T) { require.Nil(t, err) // Check the result's contents - require.Equal(t, "Reserved an account", response.Message) + require.Equal(t, "Your account has been initialized successfully", response.Message) require.True(t, response.Success) } -func TestAccountsCreateQueueReservationUsernameReserved(t *testing.T) { +func TestAccountsCreateSetupInvalidUsername(t *testing.T) { result, err := goreq.Request{ Method: "POST", Uri: server.URL + "/accounts", ContentType: "application/json", Body: routes.AccountsCreateRequest{ - AltEmail: "not-reserved@example.com", - Username: "reserved", + Username: "kekkekek", + InviteCode: verificationTokenID, + Password: accountPassword, }, }.Do() require.Nil(t, err) @@ -377,18 +400,19 @@ func TestAccountsCreateQueueReservationUsernameReserved(t *testing.T) { require.Nil(t, err) // Check the result's contents - require.Equal(t, "Username already reserved", response.Message) + require.Equal(t, "Invalid username", response.Message) require.False(t, response.Success) } -func TestAccountsCreateQueueReservationUsernameTaken(t *testing.T) { +func TestAccountsCreateSetupInvalidCode(t *testing.T) { result, err := goreq.Request{ Method: "POST", Uri: server.URL + "/accounts", ContentType: "application/json", Body: routes.AccountsCreateRequest{ - AltEmail: "not-reserved@example.com", - Username: "jeremy", + Username: accountUsername, + InviteCode: "kekekekek", + Password: accountPassword, }, }.Do() require.Nil(t, err) @@ -399,19 +423,28 @@ func TestAccountsCreateQueueReservationUsernameTaken(t *testing.T) { require.Nil(t, err) // Check the result's contents + require.Equal(t, "Invalid invitation code", response.Message) require.False(t, response.Success) - require.Equal(t, "Username already used", response.Message) } -func TestAccountsCreateQueueReservationDisabled(t *testing.T) { - env.Config.UsernameReservation = false +func TestAccountsCreateSetupNotOwnedCode(t *testing.T) { + verificationToken := models.Token{ + Resource: models.MakeResource("top kek", "test verification token"), + Type: "verify", + } + verificationToken.ExpireSoon() + + err := env.Tokens.Insert(verificationToken) + require.Nil(t, err) + result, err := goreq.Request{ Method: "POST", Uri: server.URL + "/accounts", ContentType: "application/json", Body: routes.AccountsCreateRequest{ - AltEmail: "something@example.com", - Username: "something", + Username: accountUsername, + InviteCode: verificationToken.ID, + Password: accountPassword, }, }.Do() require.Nil(t, err) @@ -422,19 +455,28 @@ func TestAccountsCreateQueueReservationDisabled(t *testing.T) { require.Nil(t, err) // Check the result's contents + require.Equal(t, "Invalid invitation code", response.Message) require.False(t, response.Success) - require.Equal(t, "Username reservation is disabled", response.Message) - env.Config.UsernameReservation = true } -func TestAccountsCreateQueueClassicUsedEmail(t *testing.T) { - // POST /accounts - queue +func TestAccountsCreateSetupNotVerify(t *testing.T) { + verificationToken := models.Token{ + Resource: models.MakeResource(accountID, "test verification token"), + Type: "notverify", + } + verificationToken.ExpireSoon() + + err := env.Tokens.Insert(verificationToken) + require.Nil(t, err) + result, err := goreq.Request{ Method: "POST", Uri: server.URL + "/accounts", ContentType: "application/json", Body: routes.AccountsCreateRequest{ - AltEmail: "something@example.com", + Username: accountUsername, + InviteCode: verificationToken.ID, + Password: accountPassword, }, }.Do() require.Nil(t, err) @@ -445,18 +487,28 @@ func TestAccountsCreateQueueClassicUsedEmail(t *testing.T) { require.Nil(t, err) // Check the result's contents - require.Equal(t, "Email already used for a reservation", response.Message) + require.Equal(t, "Invalid invitation code", response.Message) require.False(t, response.Success) } -func TestAccountsCreateQueueClassicReservedEmail(t *testing.T) { - // POST /accounts - queue +func TestAccountsCreateSetupCodeExpired(t *testing.T) { + verificationToken := models.Token{ + Resource: models.MakeResource(accountID, "test verification token"), + Type: "verify", + } + verificationToken.ExpiryDate = time.Now().Truncate(time.Hour * 24) + + err := env.Tokens.Insert(verificationToken) + require.Nil(t, err) + result, err := goreq.Request{ Method: "POST", Uri: server.URL + "/accounts", ContentType: "application/json", Body: routes.AccountsCreateRequest{ - AltEmail: "reserved@example.com", + Username: accountUsername, + InviteCode: verificationToken.ID, + Password: accountPassword, }, }.Do() require.Nil(t, err) @@ -467,11 +519,10 @@ func TestAccountsCreateQueueClassicReservedEmail(t *testing.T) { require.Nil(t, err) // Check the result's contents - require.Equal(t, "Email already used for a reservation", response.Message) + require.Equal(t, "Expired invitation code", response.Message) require.False(t, response.Success) } - func TestAccountsPrepareToken(t *testing.T) { // POST /accounts - classic request, err := goreq.Request{ @@ -492,8 +543,8 @@ func TestAccountsPrepareToken(t *testing.T) { require.Nil(t, err) // Check the result's contents - require.True(t, response.Success) require.Equal(t, "Authentication successful", response.Message) + require.True(t, response.Success) require.NotEmpty(t, response.Token.ID) // Populate the global token variable @@ -560,14 +611,17 @@ func TestAccountsGetNotMe(t *testing.T) { } func TestAccountUpdateMe(t *testing.T) { + newPasswordHashBytes := sha3.Sum256([]byte("cabbage123")) + newPasswordHash := string(newPasswordHashBytes[:]) + // PUT /accounts/me request := goreq.Request{ Method: "PUT", Uri: server.URL + "/accounts/me", ContentType: "application/json", Body: &routes.AccountsUpdateRequest{ - CurrentPassword: "potato", - NewPassword: "cabbage", + CurrentPassword: accountPassword, + NewPassword: newPasswordHash, AltEmail: "john.cabbage@example.com", }, } @@ -575,6 +629,8 @@ func TestAccountUpdateMe(t *testing.T) { result, err := request.Do() require.Nil(t, err) + accountPassword = newPasswordHash + // Unmarshal the response var response routes.AccountsUpdateResponse err = result.Body.FromJsonTo(&response) @@ -760,4 +816,3 @@ func TestAccountsDelete(t *testing.T) { require.Equal(t, "Your account has been successfully deleted", response.Message) require.True(t, response.Success) } -*/ diff --git a/setup/setup.go b/setup/setup.go index 31486d1..e07c343 100644 --- a/setup/setup.go +++ b/setup/setup.go @@ -622,6 +622,12 @@ func PrepareMux(flags *env.Flags) *web.Mux { w := httptest.NewRecorder() r, err := http.NewRequest(input.Method, "http://api.lavaboom.io"+input.Path, strings.NewReader(input.Body)) if err != nil { + env.Log.WithFields(logrus.Fields{ + "id": session.ID(), + "error": err.Error(), + "path": input.Path, + }).Warn("SockJS request error") + // Return an error response resp, _ := json.Marshal(map[string]interface{}{ "error": err.Error(), From cdc367d5e5dcfb9fb5e2b8484a55a9c5bd5d205b Mon Sep 17 00:00:00 2001 From: Piotr Zduniak Date: Thu, 15 Jan 2015 01:21:58 +0100 Subject: [PATCH 5/6] To field in the Email model and start of the tests rewrite --- models/email.go | 2 + routes/accounts_test.go | 1496 ++++++++++++++++++--------------------- 2 files changed, 695 insertions(+), 803 deletions(-) diff --git a/models/email.go b/models/email.go index 823704a..176ea4a 100644 --- a/models/email.go +++ b/models/email.go @@ -8,6 +8,8 @@ type Email struct { // Kind of the email. Value is either sent or received. Kind string `json:"kind" gorethink:"kind"` + From []string `json:"from" gorethink:"from"` + // Who is supposed to receive the email / what email received it. To []string `json:"to" gorethink:"to"` diff --git a/routes/accounts_test.go b/routes/accounts_test.go index 4648093..39c2d49 100644 --- a/routes/accounts_test.go +++ b/routes/accounts_test.go @@ -4,8 +4,10 @@ import ( "testing" "time" + "github.com/dchest/uniuri" "github.com/franela/goreq" - "github.com/stretchr/testify/require" + . "github.com/smartystreets/goconvey/convey" + //"github.com/stretchr/testify/require" "golang.org/x/crypto/sha3" "github.com/lavab/api/env" @@ -13,806 +15,694 @@ import ( "github.com/lavab/api/routes" ) -var ( - accountUsername string - accountPassword string - accountID string - verificationTokenID string -) - -func TestAccountsCreateInvalid(t *testing.T) { - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - ContentType: "application/json", - Body: "!@#!@#", - }.Do() - require.Nil(t, err) - - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - require.False(t, response.Success) - require.Equal(t, "Invalid input format", response.Message) -} - -func TestAccountsCreateUnknown(t *testing.T) { - // POST /accounts - unknown - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - }.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check values - require.False(t, response.Success) - require.Equal(t, "Invalid request", response.Message) -} - -func TestAccountsCreateRegister(t *testing.T) { - const ( - username = "jeremy" - password = "potato" - email = "jeremy@potato.org" - ) - - // Prepare account information - accountUsername = username - passwordHash := sha3.Sum256([]byte(password)) - accountPassword = string(passwordHash[:]) - - // Prepare a token - /*inviteToken := models.Token{ - Resource: models.MakeResource("", "test invite token"), - Type: "invite", - } - inviteToken.ExpireSoon() - - err := env.Tokens.Insert(inviteToken) - require.Nil(t, err)*/ - - // POST /accounts - invited - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - ContentType: "application/json", - Body: routes.AccountsCreateRequest{ - Username: username, - AltEmail: email, - }, - }.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Your account has been added to the beta queue", response.Message) - require.True(t, response.Success) - require.NotEmpty(t, response.Account.ID) - - accountID = response.Account.ID -} - -func TestAccountsCreateInvitedExistingUsername(t *testing.T) { - const ( - username = "jeremy" - email = "jeremy@potato.org" - ) - - // POST /accounts - invited - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - ContentType: "application/json", - Body: routes.AccountsCreateRequest{ - Username: username, - AltEmail: email, - }, - }.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.False(t, response.Success) - require.Equal(t, "Username already used", response.Message) -} - -func TestAccountsCreateInvitedExistingEmail(t *testing.T) { - const ( - username = "jeremy2" - email = "jeremy@potato.org" - ) - - // POST /accounts - invited - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - ContentType: "application/json", - Body: routes.AccountsCreateRequest{ - Username: username, - AltEmail: email, - }, - }.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.False(t, response.Success) - require.Equal(t, "Email already used", response.Message) -} - -func TestAccountsCreateVerify(t *testing.T) { - // Prepare a token - verificationToken := models.Token{ - Resource: models.MakeResource(accountID, "test verification token"), - Type: "verify", - } - verificationToken.ExpireSoon() - - err := env.Tokens.Insert(verificationToken) - require.Nil(t, err) - - verificationTokenID = verificationToken.ID - - // POST /accounts - invited - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - ContentType: "application/json", - Body: routes.AccountsCreateRequest{ - Username: accountUsername, - InviteCode: verificationTokenID, - }, - }.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Valid token was provided", response.Message) - require.True(t, response.Success) -} - -func TestAccountsCreateVerifyInvalidUsername(t *testing.T) { - // POST /accounts - invited - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - ContentType: "application/json", - Body: routes.AccountsCreateRequest{ - Username: "topkek", - InviteCode: verificationTokenID, - }, - }.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Invalid username", response.Message) - require.False(t, response.Success) -} - -func TestAccountsCreateVerifyInvalidCode(t *testing.T) { - // POST /accounts - invited - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - ContentType: "application/json", - Body: routes.AccountsCreateRequest{ - Username: accountUsername, - InviteCode: "random shtuff", - }, - }.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Invalid invitation code", response.Message) - require.False(t, response.Success) -} - -func TestAccountsCreateVerifyNotOwnedCode(t *testing.T) { - // Prepare a token - verificationToken := models.Token{ - Resource: models.MakeResource("top kek", "test verification token"), - Type: "verify", - } - verificationToken.ExpireSoon() - - err := env.Tokens.Insert(verificationToken) - require.Nil(t, err) - - // POST /accounts - invited - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - ContentType: "application/json", - Body: routes.AccountsCreateRequest{ - Username: accountUsername, - InviteCode: verificationToken.ID, - }, - }.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Invalid invitation code", response.Message) - require.False(t, response.Success) -} - -func TestAccountsCreateVerifyNotVerify(t *testing.T) { - // Prepare a token - verificationToken := models.Token{ - Resource: models.MakeResource(accountID, "test verification token"), - Type: "notverify", - } - verificationToken.ExpireSoon() - - err := env.Tokens.Insert(verificationToken) - require.Nil(t, err) - - // POST /accounts - invited - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - ContentType: "application/json", - Body: routes.AccountsCreateRequest{ - Username: accountUsername, - InviteCode: verificationToken.ID, - }, - }.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Invalid invitation code", response.Message) - require.False(t, response.Success) -} - -func TestAccountsCreateVerifyExpired(t *testing.T) { - // Prepare a token - verificationToken := models.Token{ - Resource: models.MakeResource(accountID, "test verification token"), - Type: "verify", - } - verificationToken.ExpiryDate = time.Now().Truncate(time.Hour * 24) - - err := env.Tokens.Insert(verificationToken) - require.Nil(t, err) - - // POST /accounts - invited - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - ContentType: "application/json", - Body: routes.AccountsCreateRequest{ - Username: accountUsername, - InviteCode: verificationToken.ID, - }, - }.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Expired invitation code", response.Message) - require.False(t, response.Success) -} - -func TestAccountsCreateSetupWeakPassword(t *testing.T) { - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - ContentType: "application/json", - Body: routes.AccountsCreateRequest{ - Username: accountUsername, - InviteCode: verificationTokenID, - Password: "c0067d4af4e87f00dbac63b6156828237059172d1bbeac67427345d6a9fda484", - }, - }.Do() - - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Weak password", response.Message) - require.False(t, response.Success) -} - -func TestAccountsCreateSetup(t *testing.T) { - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - ContentType: "application/json", - Body: routes.AccountsCreateRequest{ - Username: accountUsername, - InviteCode: verificationTokenID, - Password: accountPassword, - }, - }.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Your account has been initialized successfully", response.Message) - require.True(t, response.Success) -} - -func TestAccountsCreateSetupInvalidUsername(t *testing.T) { - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - ContentType: "application/json", - Body: routes.AccountsCreateRequest{ - Username: "kekkekek", - InviteCode: verificationTokenID, - Password: accountPassword, - }, - }.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Invalid username", response.Message) - require.False(t, response.Success) -} - -func TestAccountsCreateSetupInvalidCode(t *testing.T) { - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - ContentType: "application/json", - Body: routes.AccountsCreateRequest{ - Username: accountUsername, - InviteCode: "kekekekek", - Password: accountPassword, - }, - }.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Invalid invitation code", response.Message) - require.False(t, response.Success) -} - -func TestAccountsCreateSetupNotOwnedCode(t *testing.T) { - verificationToken := models.Token{ - Resource: models.MakeResource("top kek", "test verification token"), - Type: "verify", - } - verificationToken.ExpireSoon() - - err := env.Tokens.Insert(verificationToken) - require.Nil(t, err) - - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - ContentType: "application/json", - Body: routes.AccountsCreateRequest{ - Username: accountUsername, - InviteCode: verificationToken.ID, - Password: accountPassword, - }, - }.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Invalid invitation code", response.Message) - require.False(t, response.Success) -} - -func TestAccountsCreateSetupNotVerify(t *testing.T) { - verificationToken := models.Token{ - Resource: models.MakeResource(accountID, "test verification token"), - Type: "notverify", - } - verificationToken.ExpireSoon() - - err := env.Tokens.Insert(verificationToken) - require.Nil(t, err) - - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - ContentType: "application/json", - Body: routes.AccountsCreateRequest{ - Username: accountUsername, - InviteCode: verificationToken.ID, - Password: accountPassword, - }, - }.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Invalid invitation code", response.Message) - require.False(t, response.Success) -} - -func TestAccountsCreateSetupCodeExpired(t *testing.T) { - verificationToken := models.Token{ - Resource: models.MakeResource(accountID, "test verification token"), - Type: "verify", - } - verificationToken.ExpiryDate = time.Now().Truncate(time.Hour * 24) - - err := env.Tokens.Insert(verificationToken) - require.Nil(t, err) - - result, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts", - ContentType: "application/json", - Body: routes.AccountsCreateRequest{ - Username: accountUsername, - InviteCode: verificationToken.ID, - Password: accountPassword, - }, - }.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsCreateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Expired invitation code", response.Message) - require.False(t, response.Success) -} - -func TestAccountsPrepareToken(t *testing.T) { - // POST /accounts - classic - request, err := goreq.Request{ - Method: "POST", - Uri: server.URL + "/tokens", - ContentType: "application/json", - Body: routes.TokensCreateRequest{ - Username: accountUsername, - Password: accountPassword, - Type: "auth", - }, - }.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.TokensCreateResponse - err = request.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Authentication successful", response.Message) - require.True(t, response.Success) - require.NotEmpty(t, response.Token.ID) - - // Populate the global token variable - authToken = response.Token.ID -} - -func TestAccountsList(t *testing.T) { - // GET /accounts - request := goreq.Request{ - Method: "GET", - Uri: server.URL + "/accounts", - } - request.AddHeader("Authorization", "Bearer "+authToken) - result, err := request.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsListResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.False(t, response.Success) - require.Equal(t, "Sorry, not implemented yet", response.Message) -} - -func TestAccountsGetMe(t *testing.T) { - // GET /accounts/me - request := goreq.Request{ - Method: "GET", - Uri: server.URL + "/accounts/me", - } - request.AddHeader("Authorization", "Bearer "+authToken) - result, err := request.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsGetResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.True(t, response.Success) - require.Equal(t, "jeremy", response.Account.Name) -} - -func TestAccountsGetNotMe(t *testing.T) { - request := goreq.Request{ - Method: "GET", - Uri: server.URL + "/accounts/not-me", - } - request.AddHeader("Authorization", "Bearer "+authToken) - result, err := request.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsGetResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.False(t, response.Success) - require.Equal(t, `Only the "me" user is implemented`, response.Message) -} - -func TestAccountUpdateMe(t *testing.T) { - newPasswordHashBytes := sha3.Sum256([]byte("cabbage123")) - newPasswordHash := string(newPasswordHashBytes[:]) - - // PUT /accounts/me - request := goreq.Request{ - Method: "PUT", - Uri: server.URL + "/accounts/me", - ContentType: "application/json", - Body: &routes.AccountsUpdateRequest{ - CurrentPassword: accountPassword, - NewPassword: newPasswordHash, - AltEmail: "john.cabbage@example.com", - }, - } - request.AddHeader("Authorization", "Bearer "+authToken) - result, err := request.Do() - require.Nil(t, err) - - accountPassword = newPasswordHash - - // Unmarshal the response - var response routes.AccountsUpdateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Your account has been successfully updated", response.Message) - require.True(t, response.Success) - require.Equal(t, "jeremy", response.Account.Name) - require.Equal(t, "john.cabbage@example.com", response.Account.AltEmail) -} - -func TestAccountUpdateInvalid(t *testing.T) { - // PUT /accounts/me - request := goreq.Request{ - Method: "PUT", - Uri: server.URL + "/accounts/me", - ContentType: "application/json", - Body: "123123123!@#!@#!@#", - } - request.AddHeader("Authorization", "Bearer "+authToken) - result, err := request.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsUpdateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Invalid input format", response.Message) - require.False(t, response.Success) -} - -func TestAccountUpdateNotMe(t *testing.T) { - // PUT /accounts/me - request := goreq.Request{ - Method: "PUT", - Uri: server.URL + "/accounts/not-me", - ContentType: "application/json", - Body: &routes.AccountsUpdateRequest{ - CurrentPassword: "potato", - NewPassword: "cabbage", - AltEmail: "john.cabbage@example.com", - }, - } - request.AddHeader("Authorization", "Bearer "+authToken) - result, err := request.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsUpdateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, `Only the "me" user is implemented`, response.Message) - require.False(t, response.Success) -} - -func TestAccountUpdateMeInvalidPassword(t *testing.T) { - // PUT /accounts/me - request := goreq.Request{ - Method: "PUT", - Uri: server.URL + "/accounts/me", - ContentType: "application/json", - Body: &routes.AccountsUpdateRequest{ - CurrentPassword: "potato2", - NewPassword: "cabbage", - AltEmail: "john.cabbage@example.com", - }, - } - request.AddHeader("Authorization", "Bearer "+authToken) - result, err := request.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsUpdateResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Invalid current password", response.Message) - require.False(t, response.Success) -} - -func TestAccountsWipeDataNotMe(t *testing.T) { - // POST /accounts/me/wipe-data - request := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts/not-me/wipe-data", - } - request.AddHeader("Authorization", "Bearer "+authToken) - result, err := request.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsWipeDataResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, `Only the "me" user is implemented`, response.Message) - require.False(t, response.Success) -} - -func TestAccountsWipeData(t *testing.T) { - // POST /accounts/me/wipe-data - request := goreq.Request{ - Method: "POST", - Uri: server.URL + "/accounts/me/wipe-data", - } - request.AddHeader("Authorization", "Bearer "+authToken) - result, err := request.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsWipeDataResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Your account has been successfully wiped", response.Message) - require.True(t, response.Success) -} - -func TestAccountsDeleteNotMe(t *testing.T) { - // Prepare a token - token := models.Token{ - Resource: models.MakeResource(accountID, "test invite token"), - Type: "auth", - } - token.ExpireSoon() - - err := env.Tokens.Insert(token) - require.Nil(t, err) - - // DELETE /accounts/me - request := goreq.Request{ - Method: "DELETE", - Uri: server.URL + "/accounts/not-me", - } - request.AddHeader("Authorization", "Bearer "+token.ID) - result, err := request.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsWipeDataResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, `Only the "me" user is implemented`, response.Message) - require.False(t, response.Success) -} - -func TestAccountsDelete(t *testing.T) { - // Prepare a token - token := models.Token{ - Resource: models.MakeResource(accountID, "test invite token"), - Type: "auth", - } - token.ExpireSoon() - - err := env.Tokens.Insert(token) - require.Nil(t, err) - - // DELETE /accounts/me - request := goreq.Request{ - Method: "DELETE", - Uri: server.URL + "/accounts/me", - } - request.AddHeader("Authorization", "Bearer "+token.ID) - result, err := request.Do() - require.Nil(t, err) - - // Unmarshal the response - var response routes.AccountsWipeDataResponse - err = result.Body.FromJsonTo(&response) - require.Nil(t, err) - - // Check the result's contents - require.Equal(t, "Your account has been successfully deleted", response.Message) - require.True(t, response.Success) +func TestAccountsRoute(t *testing.T) { + Convey("When creating a new account", t, func() { + Convey("Misformatted body should fail", func() { + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: "!@#!@#", + }.Do() + So(err, ShouldBeNil) + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Success, ShouldBeFalse) + So(response.Message, ShouldEqual, "Invalid input format") + }) + + Convey("Invalid set of data should fail", func() { + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + }.Do() + So(err, ShouldBeNil) + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Success, ShouldBeFalse) + So(response.Message, ShouldEqual, "Invalid request") + }) + + Convey("Account creation should succeed", func() { + var ( + username = uniuri.New() + password = uniuri.New() + email = uniuri.New() + "@potato.org" + ) + + passwordHash := sha3.Sum256([]byte(password)) + accountPassword := string(passwordHash[:]) + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: username, + AltEmail: email, + }, + }.Do() + So(err, ShouldBeNil) + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Your account has been added to the beta queue") + So(response.Success, ShouldBeTrue) + So(response.Account.ID, ShouldNotBeEmpty) + + account := response.Account + + Convey("Duplicating the username should fail", func() { + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: username, + AltEmail: email, + }, + }.Do() + So(err, ShouldBeNil) + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Success, ShouldBeFalse) + So(response.Message, ShouldEqual, "Username already used") + }) + + Convey("Duplicating the email should fail", func() { + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: uniuri.New(), + AltEmail: email, + }, + }.Do() + So(err, ShouldBeNil) + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Success, ShouldBeFalse) + So(response.Message, ShouldEqual, "Email already used") + }) + + Convey("Verification with an invalid username should fail", func() { + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: uniuri.New(), + InviteCode: uniuri.New(), + }, + }.Do() + So(err, ShouldBeNil) + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Invalid username") + So(response.Success, ShouldBeFalse) + }) + + Convey("Verification with an invalid code should fail", func() { + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: account.Name, + InviteCode: uniuri.New(), + }, + }.Do() + So(err, ShouldBeNil) + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Invalid invitation code") + So(response.Success, ShouldBeFalse) + }) + + Convey("Verification with a not owned code should fail", func() { + verificationToken := models.Token{ + Resource: models.MakeResource("top kek", "test verification token"), + Type: "verify", + } + verificationToken.ExpireSoon() + + err := env.Tokens.Insert(verificationToken) + So(err, ShouldBeNil) + + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: account.Name, + InviteCode: verificationToken.ID, + }, + }.Do() + So(err, ShouldBeNil) + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Invalid invitation code") + So(response.Success, ShouldBeFalse) + }) + + Convey("Verification with a token that is not a verification token should fail", func() { + verificationToken := models.Token{ + Resource: models.MakeResource(account.ID, "test verification token"), + Type: "notverify", + } + verificationToken.ExpireSoon() + + err := env.Tokens.Insert(verificationToken) + So(err, ShouldBeNil) + + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: account.Name, + InviteCode: verificationToken.ID, + }, + }.Do() + So(err, ShouldBeNil) + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Invalid invitation code") + So(response.Success, ShouldBeFalse) + }) + + Convey("Verification with an expired invitation code should fail", func() { + verificationToken := models.Token{ + Resource: models.MakeResource(account.ID, "test verification token"), + Type: "verify", + } + verificationToken.ExpiryDate = time.Now().Truncate(time.Hour * 24) + + err := env.Tokens.Insert(verificationToken) + So(err, ShouldBeNil) + + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: account.Name, + InviteCode: verificationToken.ID, + }, + }.Do() + So(err, ShouldBeNil) + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Expired invitation code") + So(response.Success, ShouldBeFalse) + }) + + Convey("Verification of the account should succeed", func() { + verificationToken := models.Token{ + Resource: models.MakeResource(account.ID, "test verification token"), + Type: "verify", + } + verificationToken.ExpireSoon() + + err := env.Tokens.Insert(verificationToken) + So(err, ShouldBeNil) + + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: username, + InviteCode: verificationToken.ID, + }, + }.Do() + So(err, ShouldBeNil) + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Valid token was provided") + So(response.Success, ShouldBeTrue) + + Convey("Setup with a weak password should fail", func() { + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: account.Name, + InviteCode: verificationToken.ID, + Password: "c0067d4af4e87f00dbac63b6156828237059172d1bbeac67427345d6a9fda484", + }, + }.Do() + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Weak password") + So(response.Success, ShouldBeFalse) + }) + + Convey("Setup with an invalid username should fail", func() { + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: uniuri.New(), + InviteCode: verificationToken.ID, + Password: accountPassword, + }, + }.Do() + So(err, ShouldBeNil) + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Invalid username") + So(response.Success, ShouldBeFalse) + }) + + Convey("Setup with an invalid code should fail", func() { + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: account.Name, + InviteCode: uniuri.New(), + Password: accountPassword, + }, + }.Do() + So(err, ShouldBeNil) + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Invalid invitation code") + So(response.Success, ShouldBeFalse) + }) + + Convey("Setup with a code that user does not own should fail", func() { + verificationToken := models.Token{ + Resource: models.MakeResource(uniuri.New(), "test verification token"), + Type: "verify", + } + verificationToken.ExpireSoon() + + err := env.Tokens.Insert(verificationToken) + So(err, ShouldBeNil) + + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: account.Name, + InviteCode: verificationToken.ID, + Password: accountPassword, + }, + }.Do() + So(err, ShouldBeNil) + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Invalid invitation code") + So(response.Success, ShouldBeFalse) + }) + + Convey("Setup with a token that is not a verification token should fail", func() { + verificationToken := models.Token{ + Resource: models.MakeResource(account.ID, "test verification token"), + Type: "notverify", + } + verificationToken.ExpireSoon() + + err := env.Tokens.Insert(verificationToken) + So(err, ShouldBeNil) + + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: account.Name, + InviteCode: verificationToken.ID, + Password: accountPassword, + }, + }.Do() + So(err, ShouldBeNil) + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Invalid invitation code") + So(response.Success, ShouldBeFalse) + }) + + Convey("Setup with a token that expired should fail", func() { + verificationToken := models.Token{ + Resource: models.MakeResource(account.ID, "test verification token"), + Type: "verify", + } + verificationToken.ExpiryDate = time.Now().Truncate(time.Hour * 24) + + err := env.Tokens.Insert(verificationToken) + So(err, ShouldBeNil) + + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: account.Name, + InviteCode: verificationToken.ID, + Password: accountPassword, + }, + }.Do() + So(err, ShouldBeNil) + + // Unmarshal the response + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + // Check the result's contents + So(response.Message, ShouldEqual, "Expired invitation code") + So(response.Success, ShouldBeFalse) + }) + + Convey("Setup with proper data should succeed", func() { + result, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts", + ContentType: "application/json", + Body: routes.AccountsCreateRequest{ + Username: account.Name, + InviteCode: verificationToken.ID, + Password: accountPassword, + }, + }.Do() + So(err, ShouldBeNil) + + var response routes.AccountsCreateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Your account has been initialized successfully") + So(response.Success, ShouldBeTrue) + + Convey("After acquiring an authentication token", func() { + request, err := goreq.Request{ + Method: "POST", + Uri: server.URL + "/tokens", + ContentType: "application/json", + Body: routes.TokensCreateRequest{ + Username: account.Name, + Password: accountPassword, + Type: "auth", + }, + }.Do() + So(err, ShouldBeNil) + + var response routes.TokensCreateResponse + err = request.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Authentication successful") + So(response.Success, ShouldBeTrue) + So(response.Token.ID, ShouldNotBeEmpty) + + authToken := response.Token.ID + + Convey("Accounts list query should return a proper response", func() { + request := goreq.Request{ + Method: "GET", + Uri: server.URL + "/accounts", + } + request.AddHeader("Authorization", "Bearer "+authToken) + result, err := request.Do() + So(err, ShouldBeNil) + + var response routes.AccountsListResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Success, ShouldBeFalse) + So(response.Message, ShouldEqual, "Sorry, not implemented yet") + }) + + Convey("Getting own account information should return the account information", func() { + request := goreq.Request{ + Method: "GET", + Uri: server.URL + "/accounts/me", + } + request.AddHeader("Authorization", "Bearer "+authToken) + result, err := request.Do() + So(err, ShouldBeNil) + + var response routes.AccountsGetResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Success, ShouldBeTrue) + So(response.Account.Name, ShouldEqual, "jeremy") + }) + + Convey("Getting any non-me account should return a proper response", func() { + request := goreq.Request{ + Method: "GET", + Uri: server.URL + "/accounts/not-me", + } + request.AddHeader("Authorization", "Bearer "+authToken) + result, err := request.Do() + So(err, ShouldBeNil) + + var response routes.AccountsGetResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Success, ShouldBeFalse) + So(response.Message, ShouldEqual, `Only the "me" user is implemented`) + }) + + Convey("Updating own account should succeed", func() { + newPasswordHashBytes := sha3.Sum256([]byte("cabbage123")) + newPasswordHash := string(newPasswordHashBytes[:]) + + request := goreq.Request{ + Method: "PUT", + Uri: server.URL + "/accounts/me", + ContentType: "application/json", + Body: &routes.AccountsUpdateRequest{ + CurrentPassword: accountPassword, + NewPassword: newPasswordHash, + AltEmail: "john.cabbage@example.com", + }, + } + request.AddHeader("Authorization", "Bearer "+authToken) + result, err := request.Do() + So(err, ShouldBeNil) + + var response routes.AccountsUpdateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Your account has been successfully updated") + So(response.Success, ShouldBeTrue) + So(response.Account.Name, ShouldEqual, "jeremy") + So(response.Account.AltEmail, ShouldEqual, "john.cabbage@example.com") + }) + + Convey("Updating with an invalid body should fail", func() { + request := goreq.Request{ + Method: "PUT", + Uri: server.URL + "/accounts/me", + ContentType: "application/json", + Body: "123123123!@#!@#!@#", + } + request.AddHeader("Authorization", "Bearer "+authToken) + result, err := request.Do() + So(err, ShouldBeNil) + + var response routes.AccountsUpdateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Invalid input format") + So(response.Success, ShouldBeFalse) + }) + + Convey("Trying to update not own account should fail", func() { + request := goreq.Request{ + Method: "PUT", + Uri: server.URL + "/accounts/not-me", + ContentType: "application/json", + Body: &routes.AccountsUpdateRequest{ + CurrentPassword: "potato", + NewPassword: "cabbage", + AltEmail: "john.cabbage@example.com", + }, + } + request.AddHeader("Authorization", "Bearer "+authToken) + result, err := request.Do() + So(err, ShouldBeNil) + + var response routes.AccountsUpdateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, `Only the "me" user is implemented`) + So(response.Success, ShouldBeFalse) + }) + + Convey("Trying to update with an invalid password should fail", func() { + request := goreq.Request{ + Method: "PUT", + Uri: server.URL + "/accounts/me", + ContentType: "application/json", + Body: &routes.AccountsUpdateRequest{ + CurrentPassword: "potato2", + NewPassword: "cabbage", + AltEmail: "john.cabbage@example.com", + }, + } + request.AddHeader("Authorization", "Bearer "+authToken) + result, err := request.Do() + So(err, ShouldBeNil) + + var response routes.AccountsUpdateResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Invalid current password") + So(response.Success, ShouldBeFalse) + }) + + Convey("Wiping not own account should fail", func() { + request := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts/not-me/wipe-data", + } + request.AddHeader("Authorization", "Bearer "+authToken) + result, err := request.Do() + So(err, ShouldBeNil) + + var response routes.AccountsWipeDataResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, `Only the "me" user is implemented`) + So(response.Success, ShouldBeFalse) + }) + + Convey("Wiping own account should succeed", func() { + request := goreq.Request{ + Method: "POST", + Uri: server.URL + "/accounts/me/wipe-data", + } + request.AddHeader("Authorization", "Bearer "+authToken) + result, err := request.Do() + So(err, ShouldBeNil) + + var response routes.AccountsWipeDataResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Your account has been successfully wiped") + So(response.Success, ShouldBeTrue) + }) + + Convey("Deleting not own account should fail", func() { + token := models.Token{ + Resource: models.MakeResource(account.ID, "test invite token"), + Type: "auth", + } + token.ExpireSoon() + + err := env.Tokens.Insert(token) + So(err, ShouldBeNil) + + request := goreq.Request{ + Method: "DELETE", + Uri: server.URL + "/accounts/not-me", + } + request.AddHeader("Authorization", "Bearer "+token.ID) + result, err := request.Do() + So(err, ShouldBeNil) + + var response routes.AccountsWipeDataResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, `Only the "me" user is implemented`) + So(response.Success, ShouldBeFalse) + }) + + Convey("Deleting own account should succeed", func() { + token := models.Token{ + Resource: models.MakeResource(account.ID, "test invite token"), + Type: "auth", + } + token.ExpireSoon() + + err := env.Tokens.Insert(token) + So(err, ShouldBeNil) + + request := goreq.Request{ + Method: "DELETE", + Uri: server.URL + "/accounts/me", + } + request.AddHeader("Authorization", "Bearer "+token.ID) + result, err := request.Do() + So(err, ShouldBeNil) + + var response routes.AccountsWipeDataResponse + err = result.Body.FromJsonTo(&response) + So(err, ShouldBeNil) + + So(response.Message, ShouldEqual, "Your account has been successfully deleted") + So(response.Success, ShouldBeTrue) + }) + }) + }) + }) + }) + }) } From cb47c48a407ed9dab3e4e57a4319e3659bc4ac41 Mon Sep 17 00:00:00 2001 From: Piotr Zduniak Date: Thu, 15 Jan 2015 01:25:08 +0100 Subject: [PATCH 6/6] Tests fix --- routes/accounts_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/routes/accounts_test.go b/routes/accounts_test.go index 39c2d49..f072dc4 100644 --- a/routes/accounts_test.go +++ b/routes/accounts_test.go @@ -502,7 +502,7 @@ func TestAccountsRoute(t *testing.T) { So(err, ShouldBeNil) So(response.Success, ShouldBeTrue) - So(response.Account.Name, ShouldEqual, "jeremy") + So(response.Account.Name, ShouldEqual, account.Name) }) Convey("Getting any non-me account should return a proper response", func() { @@ -546,7 +546,6 @@ func TestAccountsRoute(t *testing.T) { So(response.Message, ShouldEqual, "Your account has been successfully updated") So(response.Success, ShouldBeTrue) - So(response.Account.Name, ShouldEqual, "jeremy") So(response.Account.AltEmail, ShouldEqual, "john.cabbage@example.com") })