1
0
mirror of https://github.com/raseels-repos/golang-saas-starter-kit.git synced 2025-06-04 23:37:49 +02:00

1016 lines
34 KiB
Go

package user_auth
import (
"encoding/json"
"fmt"
"os"
"testing"
"time"
"geeks-accelerator/oss/saas-starter-kit/internal/account"
"geeks-accelerator/oss/saas-starter-kit/internal/account/account_preference"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/auth"
"geeks-accelerator/oss/saas-starter-kit/internal/platform/tests"
"geeks-accelerator/oss/saas-starter-kit/internal/user"
"geeks-accelerator/oss/saas-starter-kit/internal/user_account"
"github.com/google/go-cmp/cmp"
"github.com/pborman/uuid"
"github.com/pkg/errors"
)
var (
test *tests.Test
repo *Repository
)
// TestMain is the entry point for testing.
func TestMain(m *testing.M) {
os.Exit(testMain(m))
}
func testMain(m *testing.M) int {
test = tests.New()
defer test.TearDown()
tknGen := &auth.MockTokenGenerator{}
userRepo := user.MockRepository(test.MasterDB)
userAccRepo := user_account.NewRepository(test.MasterDB)
accPrefRepo := account_preference.NewRepository(test.MasterDB)
repo = NewRepository(test.MasterDB, tknGen, userRepo, userAccRepo, accPrefRepo)
return m.Run()
}
// TestAuthenticate validates the behavior around authenticating users.
func TestAuthenticate(t *testing.T) {
defer tests.Recover(t)
t.Log("Given the need to authenticate users")
{
t.Log("\tWhen handling a single User.")
{
ctx := tests.Context()
// Auth tokens are valid for an our and is verified against current time.
// Issue the token one hour ago.
now := time.Now().Add(time.Hour * -1)
// Try to authenticate an invalid user.
_, err := repo.Authenticate(ctx,
AuthenticateRequest{
Email: "doesnotexist@gmail.com",
Password: "xy7",
}, time.Hour, now)
if errors.Cause(err) != ErrAuthenticationFailure {
t.Logf("\t\tGot : %+v", err)
t.Logf("\t\tWant: %+v", ErrAuthenticationFailure)
t.Fatalf("\t%s\tAuthenticate non existant user failed.", tests.Failed)
}
t.Logf("\t%s\tAuthenticate non existant user ok.", tests.Success)
// Create a new user for testing.
usrAcc, err := user_account.MockUserAccount(ctx, test.MasterDB, now, user_account.UserAccountRole_User)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate user account failed.", tests.Failed)
}
t.Logf("\t%s\tCreate user account ok.", tests.Success)
// Add 30 minutes to now to simulate time passing.
now = now.Add(time.Minute * 30)
acc2, err := account.MockAccount(ctx, test.MasterDB, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate second account failed.", tests.Failed)
}
t.Logf("\t%s\tCreate second account ok.", tests.Success)
// Associate second new account with user user. Need to ensure that now
// is always greater than the first user_account entry created so it will
// be returned consistently back in the same order, last.
account2Role := auth.RoleUser
_, err = repo.UserAccount.Create(ctx, auth.Claims{}, user_account.UserAccountCreateRequest{
UserID: usrAcc.UserID,
AccountID: acc2.ID,
Roles: []user_account.UserAccountRole{user_account.UserAccountRole(account2Role)},
}, now)
// Add 30 minutes to now to simulate time passing.
now = now.Add(time.Minute * 5)
// Try to authenticate valid user with invalid password.
_, err = repo.Authenticate(ctx,
AuthenticateRequest{
Email: usrAcc.User.Email,
Password: "xy7",
},
time.Hour, now)
if errors.Cause(err) != ErrAuthenticationFailure {
t.Logf("\t\tGot : %+v", err)
t.Logf("\t\tWant: %+v", ErrAuthenticationFailure)
t.Fatalf("\t%s\tAuthenticate user w/invalid password failed.", tests.Failed)
}
t.Logf("\t%s\tAuthenticate user w/invalid password ok.", tests.Success)
// Verify that the user can be authenticated with the created user.
tkn1, err := repo.Authenticate(ctx,
AuthenticateRequest{
Email: usrAcc.User.Email,
Password: usrAcc.User.Password,
}, time.Hour, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tAuthenticate user failed.", tests.Failed)
}
t.Logf("\t%s\tAuthenticate user ok.", tests.Success)
// Ensure the token string was correctly generated.
claims1, err := repo.TknGen.ParseClaims(tkn1.AccessToken)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tParse claims from token failed.", tests.Failed)
}
expectClaims := tkn1.claims
expectClaims.RootUserID = ""
expectClaims.RootAccountID = ""
expectClaims.Subject = usrAcc.UserID
expectClaims.Audience = usrAcc.AccountID
if diff := cmpClaims(claims1, expectClaims); diff != "" {
t.Fatalf("\t%s\tExpected parsed claims to match from token. Diff:\n%s", tests.Failed, diff)
}
t.Logf("\t%s\tAuthenticate parse claims from token ok.", tests.Success)
// Try switching to a second account using the first set of claims.
tkn2, err := repo.SwitchAccount(ctx, claims1,
SwitchAccountRequest{AccountID: acc2.ID}, time.Hour, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tSwitchAccount user failed.", tests.Failed)
}
t.Logf("\t%s\tSwitchAccount user ok.", tests.Success)
// Ensure the token string was correctly generated.
claims2, err := repo.TknGen.ParseClaims(tkn2.AccessToken)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tParse claims from token failed.", tests.Failed)
}
expectClaims = tkn2.claims
expectClaims.RootUserID = usrAcc.UserID
expectClaims.RootAccountID = acc2.ID
expectClaims.Subject = usrAcc.UserID
expectClaims.Audience = acc2.ID
if diff := cmpClaims(claims2, expectClaims); diff != "" {
t.Fatalf("\t%s\tExpected parsed claims to match from token. Diff:\n%s", tests.Failed, diff)
}
t.Logf("\t%s\tSwitchAccount parse claims from token ok.", tests.Success)
}
}
}
// TestUserUpdatePassword validates update user password works.
func TestUserUpdatePassword(t *testing.T) {
t.Log("Given the need ensure a user password can be updated.")
{
ctx := tests.Context()
now := time.Date(2018, time.October, 1, 0, 0, 0, 0, time.UTC)
// Create a new user for testing.
usrAcc, err := user_account.MockUserAccount(ctx, test.MasterDB, now, user_account.UserAccountRole_User)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate user account failed.", tests.Failed)
}
t.Logf("\t%s\tCreate user account ok.", tests.Success)
// Verify that the user can be authenticated with the created user.
_, err = repo.Authenticate(ctx,
AuthenticateRequest{
Email: usrAcc.User.Email,
Password: usrAcc.User.Password,
}, time.Hour, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tAuthenticate failed.", tests.Failed)
}
// Update the users password.
newPass := uuid.NewRandom().String()
err = repo.User.UpdatePassword(ctx, auth.Claims{}, user.UserUpdatePasswordRequest{
ID: usrAcc.UserID,
Password: newPass,
PasswordConfirm: newPass,
}, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tUpdate password failed.", tests.Failed)
}
t.Logf("\t%s\tUpdatePassword ok.", tests.Success)
// Verify that the user can be authenticated with the updated password.
_, err = repo.Authenticate(ctx,
AuthenticateRequest{
Email: usrAcc.User.Email,
Password: newPass,
}, time.Hour, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tAuthenticate failed.", tests.Failed)
}
t.Logf("\t%s\tAuthenticate ok.", tests.Success)
}
}
// TestUserResetPassword validates that reset password for a user works.
func TestUserResetPassword(t *testing.T) {
t.Log("Given the need ensure a user can reset their password.")
{
ctx := tests.Context()
now := time.Date(2018, time.October, 1, 0, 0, 0, 0, time.UTC)
// Create a new user for testing.
usrAcc, err := user_account.MockUserAccount(ctx, test.MasterDB, now, user_account.UserAccountRole_User)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate user account failed.", tests.Failed)
}
t.Logf("\t%s\tCreate user account ok.", tests.Success)
ttl := time.Hour
// Make the reset password request.
resetHash, err := repo.User.ResetPassword(ctx, user.UserResetPasswordRequest{
Email: usrAcc.User.Email,
TTL: ttl,
}, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tResetPassword failed.", tests.Failed)
}
t.Logf("\t%s\tResetPassword ok.", tests.Success)
// Assuming we have received the email and clicked the link, we now can ensure confirm works.
newPass := uuid.NewRandom().String()
reset, err := repo.User.ResetConfirm(ctx, user.UserResetConfirmRequest{
ResetHash: resetHash,
Password: newPass,
PasswordConfirm: newPass,
}, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tResetConfirm failed.", tests.Failed)
} else if reset.ID != usrAcc.User.ID {
t.Logf("\t\tGot : %+v", reset.ID)
t.Logf("\t\tWant: %+v", usrAcc.User.ID)
t.Fatalf("\t%s\tResetConfirm failed.", tests.Failed)
}
t.Logf("\t%s\tResetConfirm ok.", tests.Success)
// Verify that the user can be authenticated with the updated password.
_, err = repo.Authenticate(ctx,
AuthenticateRequest{
Email: usrAcc.User.Email,
Password: newPass,
}, time.Hour, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tAuthenticate failed.", tests.Failed)
}
t.Logf("\t%s\tAuthenticate ok.", tests.Success)
}
}
// TestSwitchAccount validates the behavior around allowing users to switch between their accounts.
func TestSwitchAccount(t *testing.T) {
defer tests.Recover(t)
// Auth tokens are valid for an our and is verified against current time.
// Issue the token one hour ago.
now := time.Now().Add(time.Hour * -1)
ctx := tests.Context()
type authTest struct {
name string
root *user_account.MockUserAccountResponse
switch1Req SwitchAccountRequest
switch1Roles []user_account.UserAccountRole
switch1Scopes []string
switch1Err error
switch2Req SwitchAccountRequest
switch2Roles []user_account.UserAccountRole
switch2Scopes []string
switch2Err error
}
var authTests []authTest
// Test all the combinations there the user has access to all three accounts.
if true {
for _, roles := range [][]user_account.UserAccountRole{
[]user_account.UserAccountRole{user_account.UserAccountRole_Admin, user_account.UserAccountRole_Admin, user_account.UserAccountRole_Admin},
[]user_account.UserAccountRole{user_account.UserAccountRole_User, user_account.UserAccountRole_User, user_account.UserAccountRole_User},
[]user_account.UserAccountRole{user_account.UserAccountRole_Admin, user_account.UserAccountRole_User, user_account.UserAccountRole_Admin},
[]user_account.UserAccountRole{user_account.UserAccountRole_User, user_account.UserAccountRole_Admin, user_account.UserAccountRole_User},
} {
// Create a new user for testing.
usrAcc, err := user_account.MockUserAccount(ctx, test.MasterDB, now, roles[0])
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate user account failed.", tests.Failed)
}
// Create the second account.
now = now.Add(time.Minute)
acc2, err := account.MockAccount(ctx, test.MasterDB, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate second account failed.", tests.Failed)
}
// Associate the second account with root user.
usrAcc2, err := repo.UserAccount.Create(ctx, auth.Claims{}, user_account.UserAccountCreateRequest{
UserID: usrAcc.UserID,
AccountID: acc2.ID,
Roles: []user_account.UserAccountRole{user_account.UserAccountRole(roles[1])},
}, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tLinking second account to user failed.", tests.Failed)
}
// Create the third account.
now = now.Add(time.Minute)
acc3, err := account.MockAccount(ctx, test.MasterDB, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate third account failed.", tests.Failed)
}
// Associate the third account with root user.
usrAcc3, err := repo.UserAccount.Create(ctx, auth.Claims{}, user_account.UserAccountCreateRequest{
UserID: usrAcc.UserID,
AccountID: acc3.ID,
Roles: []user_account.UserAccountRole{user_account.UserAccountRole(roles[2])},
}, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tLinking third account to user failed.", tests.Failed)
}
authTests = append(authTests, authTest{
name: fmt.Sprintf("Root account role %s -> role %s account 2 -> role %s account 3.",
roles[0], roles[1], roles[2]),
root: usrAcc,
switch1Req: SwitchAccountRequest{AccountID: acc2.ID},
switch1Roles: usrAcc2.Roles,
switch1Err: nil,
switch2Req: SwitchAccountRequest{AccountID: acc3.ID},
switch2Err: nil,
switch2Roles: usrAcc3.Roles,
})
}
}
// Root account 1 -> invalid account 2
if true {
// Create a new user for testing.
usrAcc, err := user_account.MockUserAccount(ctx, test.MasterDB, now, user_account.UserAccountRole_Admin)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate user account failed.", tests.Failed)
}
// Create the second account and don't associate it with the root user.
now = now.Add(time.Minute)
acc2, err := account.MockAccount(ctx, test.MasterDB, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate second account failed.", tests.Failed)
}
authTests = append(authTests, authTest{
name: "Root account 1 -> invalid account 2.",
root: usrAcc,
switch1Req: SwitchAccountRequest{AccountID: acc2.ID},
switch1Err: ErrAuthenticationFailure,
})
}
// Root account 1 -> valid account 2 with scopes -> valid account 3 with invalid scope.
if true {
// Create a new user for testing.
usrAcc, err := user_account.MockUserAccount(ctx, test.MasterDB, now, user_account.UserAccountRole_Admin)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate user account failed.", tests.Failed)
}
// Create the second account.
now = now.Add(time.Minute)
acc2, err := account.MockAccount(ctx, test.MasterDB, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate second account failed.", tests.Failed)
}
// Associate the second account with root user.
usrAcc2, err := repo.UserAccount.Create(ctx, auth.Claims{}, user_account.UserAccountCreateRequest{
UserID: usrAcc.UserID,
AccountID: acc2.ID,
Roles: []user_account.UserAccountRole{user_account.UserAccountRole_Admin},
}, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tLinking second account to user failed.", tests.Failed)
}
// Create the third account.
now = now.Add(time.Minute)
acc3, err := account.MockAccount(ctx, test.MasterDB, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate third account failed.", tests.Failed)
}
// Associate the third account with root user.
usrAcc3, err := repo.UserAccount.Create(ctx, auth.Claims{}, user_account.UserAccountCreateRequest{
UserID: usrAcc.UserID,
AccountID: acc3.ID,
Roles: []user_account.UserAccountRole{user_account.UserAccountRole_User},
}, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tLinking third account to user failed.", tests.Failed)
}
authTests = append(authTests, authTest{
name: "Root account 1 -> valid account 2 with scopes -> valid account 3 with invalid scope.",
root: usrAcc,
switch1Req: SwitchAccountRequest{AccountID: acc2.ID},
switch1Roles: usrAcc2.Roles,
switch1Scopes: []string{user_account.UserAccountRole_User.String()},
switch1Err: nil,
switch2Req: SwitchAccountRequest{AccountID: acc3.ID},
switch2Roles: usrAcc3.Roles,
switch2Scopes: []string{user_account.UserAccountRole_Admin.String()},
switch2Err: ErrForbidden,
})
}
// Add 30 minutes to now to simulate time passing.
now = now.Add(time.Minute * 5)
t.Log("Given the need to switch accounts.")
{
for i, authTest := range authTests {
t.Logf("\tTest: %d\tWhen running test: %s", i, authTest.name)
{
// Verify that the user can be authenticated with the created user.
var claims1 auth.Claims
tkn1, err := repo.Authenticate(ctx,
AuthenticateRequest{
Email: authTest.root.User.Email,
Password: authTest.root.User.Password,
}, time.Hour, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tAuthenticate user failed.", tests.Failed)
} else {
// Ensure the token string was correctly generated.
claims1, err = repo.TknGen.ParseClaims(tkn1.AccessToken)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tParse claims from token failed.", tests.Failed)
}
expectClaims := tkn1.claims
expectClaims.RootUserID = ""
expectClaims.RootAccountID = ""
expectClaims.Subject = authTest.root.UserID
expectClaims.Audience = authTest.root.AccountID
expectClaims.Roles = rolesStringSlice(authTest.root.Roles)
if diff := cmpClaims(claims1, expectClaims); diff != "" {
t.Fatalf("\t%s\tExpected parsed claims to match from token. Diff:\n%s", tests.Failed, diff)
}
}
t.Logf("\t%s\tAuthenticate root user with role %v ok.", tests.Success, authTest.root.Roles)
// Try to switch to account 2.
var claims2 auth.Claims
tkn2, err := repo.SwitchAccount(ctx, claims1, authTest.switch1Req, time.Hour, now, authTest.switch1Scopes...)
if err != authTest.switch1Err {
if errors.Cause(err) != authTest.switch1Err {
t.Log("\t\tExpected :", authTest.switch1Err)
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tSwitchAccount account 1 with role %v failed.", tests.Failed, authTest.switch1Roles)
}
} else {
// Ensure the token string was correctly generated.
claims2, err = repo.TknGen.ParseClaims(tkn2.AccessToken)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tParse claims from token failed.", tests.Failed)
}
expectClaims := tkn2.claims
expectClaims.RootUserID = authTest.root.UserID
expectClaims.RootAccountID = authTest.switch1Req.AccountID
expectClaims.Subject = authTest.root.UserID
expectClaims.Audience = authTest.switch1Req.AccountID
if len(authTest.switch1Scopes) > 0 {
expectClaims.Roles = authTest.switch1Scopes
} else {
expectClaims.Roles = rolesStringSlice(authTest.switch1Roles)
}
if diff := cmpClaims(claims2, expectClaims); diff != "" {
t.Fatalf("\t%s\tExpected parsed claims to match from token. Diff:\n%s", tests.Failed, diff)
}
}
t.Logf("\t%s\tSwitchAccount account 1 with role %v ok.", tests.Success, authTest.switch1Roles)
// If the user can't login, don't need to test any further.
if authTest.switch1Err != nil || authTest.switch2Req.AccountID == "" {
continue
}
// Try to switch to account 3.
tkn3, err := repo.SwitchAccount(ctx, claims2, authTest.switch2Req, time.Hour, now, authTest.switch2Scopes...)
if err != authTest.switch2Err {
if errors.Cause(err) != authTest.switch2Err {
t.Log("\t\tExpected :", authTest.switch2Err)
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tSwitchAccount account 2 with role %v failed.", tests.Failed, authTest.switch2Roles)
}
} else {
// Ensure the token string was correctly generated.
claims3, err := repo.TknGen.ParseClaims(tkn3.AccessToken)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tParse claims from token failed.", tests.Failed)
}
expectClaims := tkn3.claims
expectClaims.RootUserID = authTest.root.UserID
expectClaims.RootAccountID = authTest.switch2Req.AccountID
expectClaims.Subject = authTest.root.UserID
expectClaims.Audience = authTest.switch2Req.AccountID
if len(authTest.switch2Scopes) > 0 {
expectClaims.Roles = authTest.switch2Scopes
} else {
expectClaims.Roles = rolesStringSlice(authTest.switch2Roles)
}
if diff := cmpClaims(claims3, expectClaims); diff != "" {
t.Fatalf("\t%s\tExpected parsed claims to match from token. Diff:\n%s", tests.Failed, diff)
}
}
t.Logf("\t%s\tSwitchAccount account 2 with role %v ok.", tests.Success, authTest.switch2Roles)
}
}
}
}
// TestVirtualLogin validates the behavior around allowing users to virtual login users.
func TestVirtualLogin(t *testing.T) {
defer tests.Recover(t)
// Auth tokens are valid for an our and is verified against current time.
// Issue the token one hour ago.
now := time.Now().Add(time.Hour * -1)
ctx := tests.Context()
type authTest struct {
name string
root *user_account.MockUserAccountResponse
login1Req VirtualLoginRequest
login1Err error
login1Role user_account.UserAccountRole
login2Req VirtualLoginRequest
login2Err error
login2Role user_account.UserAccountRole
login2Logout bool
}
var authTests []authTest
// Root admin -> role admin -> role admin
{
// Create a new user for testing.
usrAcc, err := user_account.MockUserAccount(ctx, test.MasterDB, now, user_account.UserAccountRole_Admin)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate user account failed.", tests.Failed)
}
usr2, err := user.MockUser(ctx, test.MasterDB, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate second account failed.", tests.Failed)
}
// Associate second user with basic role associated with the same account.
usrAcc2, err := repo.UserAccount.Create(ctx, auth.Claims{}, user_account.UserAccountCreateRequest{
UserID: usr2.ID,
AccountID: usrAcc.AccountID,
Roles: []user_account.UserAccountRole{user_account.UserAccountRole(user_account.UserAccountRole_Admin)},
}, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tLinking second user to account failed.", tests.Failed)
}
usr3, err := user.MockUser(ctx, test.MasterDB, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate second account failed.", tests.Failed)
}
// Associate second user with basic role associated with the same account.
usrAcc3, err := repo.UserAccount.Create(ctx, auth.Claims{}, user_account.UserAccountCreateRequest{
UserID: usr3.ID,
AccountID: usrAcc.AccountID,
Roles: []user_account.UserAccountRole{user_account.UserAccountRole(user_account.UserAccountRole_Admin)},
}, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tLinking third user to account failed.", tests.Failed)
}
authTests = append(authTests, authTest{
name: "Root admin -> role admin -> role admin",
root: usrAcc,
login1Req: VirtualLoginRequest{
UserID: usr2.ID,
AccountID: usrAcc.AccountID,
},
login1Role: usrAcc2.Roles[0],
login1Err: nil,
login2Req: VirtualLoginRequest{
UserID: usr3.ID,
AccountID: usrAcc.AccountID,
},
login2Err: nil,
login2Role: usrAcc3.Roles[0],
login2Logout: true,
})
}
// Root admin -> role admin -> role user
if true {
// Create a new user for testing.
usrAcc, err := user_account.MockUserAccount(ctx, test.MasterDB, now, user_account.UserAccountRole_Admin)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate user account failed.", tests.Failed)
}
usr2, err := user.MockUser(ctx, test.MasterDB, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate second account failed.", tests.Failed)
}
// Associate second user with basic role associated with the same account.
usrAcc2, err := repo.UserAccount.Create(ctx, auth.Claims{}, user_account.UserAccountCreateRequest{
UserID: usr2.ID,
AccountID: usrAcc.AccountID,
Roles: []user_account.UserAccountRole{user_account.UserAccountRole(user_account.UserAccountRole_Admin)},
}, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tLinking second user to account failed.", tests.Failed)
}
usr3, err := user.MockUser(ctx, test.MasterDB, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate second account failed.", tests.Failed)
}
// Associate second user with basic role associated with the same account.
usrAcc3, err := repo.UserAccount.Create(ctx, auth.Claims{}, user_account.UserAccountCreateRequest{
UserID: usr3.ID,
AccountID: usrAcc.AccountID,
Roles: []user_account.UserAccountRole{user_account.UserAccountRole(user_account.UserAccountRole_User)},
}, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tLinking third user to account failed.", tests.Failed)
}
authTests = append(authTests, authTest{
name: "Root admin -> role admin -> role user",
root: usrAcc,
login1Req: VirtualLoginRequest{
UserID: usr2.ID,
AccountID: usrAcc.AccountID,
},
login1Err: nil,
login1Role: usrAcc2.Roles[0],
login2Req: VirtualLoginRequest{
UserID: usr3.ID,
AccountID: usrAcc.AccountID,
},
login2Err: nil,
login2Role: usrAcc3.Roles[0],
login2Logout: true,
})
}
// Root admin -> role user -> role admin
if true {
// Create a new user for testing.
usrAcc, err := user_account.MockUserAccount(ctx, test.MasterDB, now, user_account.UserAccountRole_Admin)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate user account failed.", tests.Failed)
}
usr2, err := user.MockUser(ctx, test.MasterDB, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate second account failed.", tests.Failed)
}
// Associate second user with basic role associated with the same account.
usrAcc2, err := repo.UserAccount.Create(ctx, auth.Claims{}, user_account.UserAccountCreateRequest{
UserID: usr2.ID,
AccountID: usrAcc.AccountID,
Roles: []user_account.UserAccountRole{user_account.UserAccountRole(user_account.UserAccountRole_User)},
}, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tLinking second user to account failed.", tests.Failed)
}
usr3, err := user.MockUser(ctx, test.MasterDB, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate second account failed.", tests.Failed)
}
// Associate second user with basic role associated with the same account.
usrAcc3, err := repo.UserAccount.Create(ctx, auth.Claims{}, user_account.UserAccountCreateRequest{
UserID: usr3.ID,
AccountID: usrAcc.AccountID,
Roles: []user_account.UserAccountRole{user_account.UserAccountRole(user_account.UserAccountRole_Admin)},
}, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tLinking third user to account failed.", tests.Failed)
}
authTests = append(authTests, authTest{
name: "Root admin -> role user -> role admin",
root: usrAcc,
login1Req: VirtualLoginRequest{
UserID: usr2.ID,
AccountID: usrAcc.AccountID,
},
login1Err: nil,
login1Role: usrAcc2.Roles[0],
login2Req: VirtualLoginRequest{
UserID: usr3.ID,
AccountID: usrAcc.AccountID,
},
login2Err: ErrForbidden,
login2Role: usrAcc3.Roles[0],
login2Logout: true,
})
}
// Root user -> role admin
if true {
// Create a new user for testing.
usrAcc, err := user_account.MockUserAccount(ctx, test.MasterDB, now, user_account.UserAccountRole_User)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate user account failed.", tests.Failed)
}
usr2, err := user.MockUser(ctx, test.MasterDB, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate second account failed.", tests.Failed)
}
// Associate second user with basic role associated with the same account.
usrAcc2, err := repo.UserAccount.Create(ctx, auth.Claims{}, user_account.UserAccountCreateRequest{
UserID: usr2.ID,
AccountID: usrAcc.AccountID,
Roles: []user_account.UserAccountRole{user_account.UserAccountRole(user_account.UserAccountRole_Admin)},
}, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tLinking second user to account failed.", tests.Failed)
}
authTests = append(authTests, authTest{
name: "Root user -> role admin",
root: usrAcc,
login1Req: VirtualLoginRequest{
UserID: usr2.ID,
AccountID: usrAcc.AccountID,
},
login1Err: ErrForbidden,
login1Role: usrAcc2.Roles[0],
login2Logout: true,
})
}
// Root user -> role user
if true {
// Create a new user for testing.
usrAcc, err := user_account.MockUserAccount(ctx, test.MasterDB, now, user_account.UserAccountRole_User)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate user account failed.", tests.Failed)
}
usr2, err := user.MockUser(ctx, test.MasterDB, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tCreate second account failed.", tests.Failed)
}
// Associate second user with basic role associated with the same account.
usrAcc2, err := repo.UserAccount.Create(ctx, auth.Claims{}, user_account.UserAccountCreateRequest{
UserID: usr2.ID,
AccountID: usrAcc.AccountID,
Roles: []user_account.UserAccountRole{user_account.UserAccountRole(user_account.UserAccountRole_User)},
}, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tLinking second user to account failed.", tests.Failed)
}
authTests = append(authTests, authTest{
name: "Root user -> role admin",
root: usrAcc,
login1Req: VirtualLoginRequest{
UserID: usr2.ID,
AccountID: usrAcc.AccountID,
},
login1Err: ErrForbidden,
login1Role: usrAcc2.Roles[0],
login2Logout: true,
})
}
// Add 30 minutes to now to simulate time passing.
now = now.Add(time.Minute * 5)
t.Log("Given the need to virtual login.")
{
for i, authTest := range authTests {
t.Logf("\tTest: %d\tWhen running test: %s", i, authTest.name)
{
// Verify that the user can be authenticated with the created user.
var claims1 auth.Claims
tkn1, err := repo.Authenticate(ctx,
AuthenticateRequest{
Email: authTest.root.User.Email,
Password: authTest.root.User.Password,
}, time.Hour, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tAuthenticate user failed.", tests.Failed)
} else {
// Ensure the token string was correctly generated.
claims1, err = repo.TknGen.ParseClaims(tkn1.AccessToken)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tParse claims from token failed.", tests.Failed)
}
expectClaims := tkn1.claims
expectClaims.RootUserID = ""
expectClaims.RootAccountID = ""
expectClaims.Subject = authTest.root.UserID
expectClaims.Audience = authTest.root.AccountID
// Hack for Unhandled Exception in go-cmp@v0.3.0/cmp/options.go:229
if diff := cmpClaims(claims1, expectClaims); diff != "" {
t.Fatalf("\t%s\tExpected parsed claims to match from token. Diff:\n%s", tests.Failed, diff)
}
}
t.Logf("\t%s\tAuthenticate root user with role %s ok.", tests.Success, authTest.root.Roles[0])
// Try virtual login to user 2.
var claims2 auth.Claims
tkn2, err := repo.VirtualLogin(ctx, claims1, authTest.login1Req, time.Hour, now)
if err != authTest.login1Err {
if errors.Cause(err) != authTest.login1Err {
t.Log("\t\tExpected :", authTest.login1Err)
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tVirtualLogin user 1 with role %s failed.", tests.Failed, authTest.login1Role)
}
} else {
// Ensure the token string was correctly generated.
claims2, err = repo.TknGen.ParseClaims(tkn2.AccessToken)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tParse claims from token failed.", tests.Failed)
}
expectClaims := tkn2.claims
expectClaims.RootUserID = authTest.root.UserID
expectClaims.RootAccountID = authTest.root.AccountID
expectClaims.Subject = authTest.login1Req.UserID
expectClaims.Audience = authTest.login1Req.AccountID
// Hack for Unhandled Exception in go-cmp@v0.3.0/cmp/options.go:229
if diff := cmpClaims(claims2, expectClaims); diff != "" {
t.Fatalf("\t%s\tExpected parsed claims to match from token. Diff:\n%s", tests.Failed, diff)
}
}
t.Logf("\t%s\tVirtualLogin user 1 with role %s ok.", tests.Success, authTest.login1Role)
// If the user can't login, don't need to test any further.
if authTest.login1Err != nil {
continue
}
// Try virtual login to user 3.
tkn3, err := repo.VirtualLogin(ctx, claims2, authTest.login2Req, time.Hour, now)
if err != authTest.login2Err {
if errors.Cause(err) != authTest.login2Err {
t.Log("\t\tExpected :", authTest.login2Err)
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tVirtualLogin user 2 with role %s failed.", tests.Failed, authTest.login2Role)
}
} else {
// Ensure the token string was correctly generated.
claims3, err := repo.TknGen.ParseClaims(tkn3.AccessToken)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tParse claims from token failed.", tests.Failed)
}
expectClaims := tkn3.claims
expectClaims.RootUserID = authTest.root.UserID
expectClaims.RootAccountID = authTest.root.AccountID
expectClaims.Subject = authTest.login2Req.UserID
expectClaims.Audience = authTest.login2Req.AccountID
// Hack for Unhandled Exception in go-cmp@v0.3.0/cmp/options.go:229
if diff := cmpClaims(claims3, expectClaims); diff != "" {
t.Fatalf("\t%s\tExpected parsed claims to match from token. Diff:\n%s", tests.Failed, diff)
}
}
t.Logf("\t%s\tVirtualLogin user 2 with role %s ok.", tests.Success, authTest.login2Role)
if authTest.login2Logout {
tknOut, err := repo.VirtualLogout(ctx, claims2, time.Hour, now)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tVirtualLogout user 2 failed.", tests.Failed)
}
// Ensure the token string was correctly generated.
claimsOut, err := repo.TknGen.ParseClaims(tknOut.AccessToken)
if err != nil {
t.Log("\t\tGot :", err)
t.Fatalf("\t%s\tParse claims from token failed.", tests.Failed)
}
expectClaims := tknOut.claims
expectClaims.RootUserID = authTest.root.UserID
expectClaims.RootAccountID = authTest.root.AccountID
expectClaims.Subject = authTest.root.UserID
expectClaims.Audience = authTest.root.AccountID
if diff := cmpClaims(claimsOut, expectClaims); diff != "" {
t.Fatalf("\t%s\tExpected parsed claims to match from token. Diff:\n%s", tests.Failed, diff)
}
t.Logf("\t%s\tVirtualLogout user 2 with role %s ok.", tests.Success, authTest.login2Role)
}
}
}
}
}
// rolesStringSlice converts a list of roles to a string slice.
func rolesStringSlice(roles []user_account.UserAccountRole) []string {
var l []string
for _, r := range roles {
l = append(l, string(r))
}
return l
}
// cmpClaims is a hack for Unhandled Exception in go-cmp@v0.3.0/cmp/options.go:229
func cmpClaims(actualClaims, expectedclaims auth.Claims) string {
dat1, _ := json.Marshal(actualClaims)
dat2, _ := json.Marshal(expectedclaims)
return cmp.Diff(string(dat1), string(dat2))
}