9d13c649a2
This change disallows creation of users possessing the same email. If a user attempts to create an account with an email address that's already used - whether it belongs to an active account or not - he will be notified of unsuccessful account creation. If he attempts to log in using an email address belonging to an inactive account, he will be presented with a link allowing him to re-send the verification email. Attempting to register with an email address belonging to an existing account triggers a password reset email. Change-Id: Iefd8c3bef00ecb1dd9e8504594607aa0dca7d82e
465 lines
14 KiB
Go
465 lines
14 KiB
Go
// Copyright (C) 2019 Storj Labs, Inc.
|
|
// See LICENSE for copying information.
|
|
|
|
package consoleql_test
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/graphql-go/graphql"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap/zaptest"
|
|
|
|
"storj.io/common/testcontext"
|
|
"storj.io/common/testrand"
|
|
"storj.io/storj/private/testplanet"
|
|
"storj.io/storj/private/testredis"
|
|
"storj.io/storj/satellite/accounting"
|
|
"storj.io/storj/satellite/accounting/live"
|
|
"storj.io/storj/satellite/analytics"
|
|
"storj.io/storj/satellite/console"
|
|
"storj.io/storj/satellite/console/consoleauth"
|
|
"storj.io/storj/satellite/console/consoleweb/consoleql"
|
|
"storj.io/storj/satellite/mailservice"
|
|
"storj.io/storj/satellite/payments/paymentsconfig"
|
|
"storj.io/storj/satellite/payments/stripecoinpayments"
|
|
"storj.io/storj/satellite/rewards"
|
|
)
|
|
|
|
func TestGraphqlQuery(t *testing.T) {
|
|
testplanet.Run(t, testplanet.Config{SatelliteCount: 1}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
|
sat := planet.Satellites[0]
|
|
db := sat.DB
|
|
log := zaptest.NewLogger(t)
|
|
|
|
partnersService := rewards.NewPartnersService(
|
|
log.Named("partners"),
|
|
rewards.DefaultPartnersDB,
|
|
)
|
|
|
|
analyticsService := analytics.NewService(log, analytics.Config{}, "test-satellite")
|
|
|
|
redis, err := testredis.Mini(ctx)
|
|
require.NoError(t, err)
|
|
defer ctx.Check(redis.Close)
|
|
|
|
cache, err := live.OpenCache(ctx, log.Named("cache"), live.Config{StorageBackend: "redis://" + redis.Addr() + "?db=0"})
|
|
require.NoError(t, err)
|
|
|
|
projectLimitCache := accounting.NewProjectLimitCache(db.ProjectAccounting(), 0, 0, 0, accounting.ProjectLimitConfig{CacheCapacity: 100})
|
|
|
|
projectUsage := accounting.NewService(db.ProjectAccounting(), cache, projectLimitCache, 5*time.Minute, -10*time.Second)
|
|
|
|
// TODO maybe switch this test to testplanet to avoid defining config and Stripe service
|
|
pc := paymentsconfig.Config{
|
|
StorageTBPrice: "10",
|
|
EgressTBPrice: "45",
|
|
SegmentPrice: "0.0000022",
|
|
}
|
|
|
|
paymentsService, err := stripecoinpayments.NewService(
|
|
log.Named("payments.stripe:service"),
|
|
stripecoinpayments.NewStripeMock(
|
|
testrand.NodeID(),
|
|
db.StripeCoinPayments().Customers(),
|
|
db.Console().Users(),
|
|
),
|
|
pc.StripeCoinPayments,
|
|
db.StripeCoinPayments(),
|
|
db.Console().Projects(),
|
|
db.ProjectAccounting(),
|
|
pc.StorageTBPrice,
|
|
pc.EgressTBPrice,
|
|
pc.SegmentPrice,
|
|
pc.BonusRate)
|
|
require.NoError(t, err)
|
|
|
|
service, err := console.NewService(
|
|
log.Named("console"),
|
|
&consoleauth.Hmac{Secret: []byte("my-suppa-secret-key")},
|
|
db.Console(),
|
|
db.ProjectAccounting(),
|
|
projectUsage,
|
|
sat.API.Buckets.Service,
|
|
partnersService,
|
|
paymentsService.Accounts(),
|
|
analyticsService,
|
|
console.Config{
|
|
PasswordCost: console.TestPasswordCost,
|
|
DefaultProjectLimit: 5,
|
|
TokenExpirationTime: 24 * time.Hour,
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
mailService, err := mailservice.New(log, &discardSender{}, "testdata")
|
|
require.NoError(t, err)
|
|
defer ctx.Check(mailService.Close)
|
|
|
|
rootObject := make(map[string]interface{})
|
|
rootObject["origin"] = "http://doesntmatter.com/"
|
|
rootObject[consoleql.ActivationPath] = "?activationToken="
|
|
rootObject[consoleql.LetUsKnowURL] = "letUsKnowURL"
|
|
rootObject[consoleql.ContactInfoURL] = "contactInfoURL"
|
|
rootObject[consoleql.TermsAndConditionsURL] = "termsAndConditionsURL"
|
|
|
|
creator := consoleql.TypeCreator{}
|
|
err = creator.Create(log, service, mailService)
|
|
require.NoError(t, err)
|
|
|
|
schema, err := graphql.NewSchema(graphql.SchemaConfig{
|
|
Query: creator.RootQuery(),
|
|
Mutation: creator.RootMutation(),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
createUser := console.CreateUser{
|
|
FullName: "John",
|
|
ShortName: "",
|
|
Email: "mtest@mail.test",
|
|
Password: "123a123",
|
|
}
|
|
|
|
regToken, err := service.CreateRegToken(ctx, 2)
|
|
require.NoError(t, err)
|
|
|
|
rootUser, err := service.CreateUser(ctx, createUser, regToken.Secret)
|
|
require.NoError(t, err)
|
|
|
|
err = paymentsService.Accounts().Setup(ctx, rootUser.ID, rootUser.Email)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("Activation", func(t *testing.T) {
|
|
activationToken, err := service.GenerateActivationToken(
|
|
ctx,
|
|
rootUser.ID,
|
|
"mtest@mail.test",
|
|
)
|
|
require.NoError(t, err)
|
|
_, err = service.ActivateAccount(ctx, activationToken)
|
|
require.NoError(t, err)
|
|
rootUser.Email = "mtest@mail.test"
|
|
})
|
|
|
|
token, err := service.Token(ctx, console.AuthUser{Email: createUser.Email, Password: createUser.Password})
|
|
require.NoError(t, err)
|
|
|
|
sauth, err := service.Authorize(consoleauth.WithAPIKey(ctx, []byte(token)))
|
|
require.NoError(t, err)
|
|
|
|
authCtx := console.WithAuth(ctx, sauth)
|
|
|
|
testQuery := func(t *testing.T, query string) interface{} {
|
|
result := graphql.Do(graphql.Params{
|
|
Schema: schema,
|
|
Context: authCtx,
|
|
RequestString: query,
|
|
RootObject: rootObject,
|
|
})
|
|
|
|
for _, err := range result.Errors {
|
|
assert.NoError(t, err)
|
|
}
|
|
require.False(t, result.HasErrors())
|
|
|
|
return result.Data
|
|
}
|
|
|
|
createdProject, err := service.CreateProject(authCtx, console.ProjectInfo{
|
|
Name: "TestProject",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// "query {project(id:\"%s\"){id,name,members(offset:0, limit:50){user{fullName,shortName,email}},apiKeys{name,id,createdAt,projectID}}}"
|
|
t.Run("Project query base info", func(t *testing.T) {
|
|
query := fmt.Sprintf(
|
|
"query {project(id:\"%s\"){id,name,description,createdAt}}",
|
|
createdProject.ID.String(),
|
|
)
|
|
|
|
result := testQuery(t, query)
|
|
|
|
data := result.(map[string]interface{})
|
|
project := data[consoleql.ProjectQuery].(map[string]interface{})
|
|
|
|
assert.Equal(t, createdProject.ID.String(), project[consoleql.FieldID])
|
|
assert.Equal(t, createdProject.Name, project[consoleql.FieldName])
|
|
assert.Equal(t, createdProject.Description, project[consoleql.FieldDescription])
|
|
|
|
createdAt := time.Time{}
|
|
err := createdAt.UnmarshalText([]byte(project[consoleql.FieldCreatedAt].(string)))
|
|
|
|
assert.NoError(t, err)
|
|
assert.True(t, createdProject.CreatedAt.Equal(createdAt))
|
|
})
|
|
|
|
regTokenUser1, err := service.CreateRegToken(ctx, 2)
|
|
require.NoError(t, err)
|
|
|
|
user1, err := service.CreateUser(authCtx, console.CreateUser{
|
|
FullName: "Mickey Last",
|
|
ShortName: "Last",
|
|
Password: "123a123",
|
|
Email: "muu1@mail.test",
|
|
}, regTokenUser1.Secret)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("Activation", func(t *testing.T) {
|
|
activationToken1, err := service.GenerateActivationToken(
|
|
ctx,
|
|
user1.ID,
|
|
"muu1@mail.test",
|
|
)
|
|
require.NoError(t, err)
|
|
_, err = service.ActivateAccount(ctx, activationToken1)
|
|
require.NoError(t, err)
|
|
user1.Email = "muu1@mail.test"
|
|
})
|
|
|
|
regTokenUser2, err := service.CreateRegToken(ctx, 2)
|
|
require.NoError(t, err)
|
|
|
|
user2, err := service.CreateUser(authCtx, console.CreateUser{
|
|
FullName: "Dubas Name",
|
|
ShortName: "Name",
|
|
Email: "muu2@mail.test",
|
|
Password: "123a123",
|
|
}, regTokenUser2.Secret)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("Activation", func(t *testing.T) {
|
|
activationToken2, err := service.GenerateActivationToken(
|
|
ctx,
|
|
user2.ID,
|
|
"muu2@mail.test",
|
|
)
|
|
require.NoError(t, err)
|
|
_, err = service.ActivateAccount(ctx, activationToken2)
|
|
require.NoError(t, err)
|
|
user2.Email = "muu2@mail.test"
|
|
})
|
|
|
|
users, err := service.AddProjectMembers(authCtx, createdProject.ID, []string{
|
|
user1.Email,
|
|
user2.Email,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 2, len(users))
|
|
|
|
t.Run("Project query team members", func(t *testing.T) {
|
|
query := fmt.Sprintf(
|
|
"query {project(id: \"%s\") {members( cursor: { limit: %d, search: \"%s\", page: %d, order: %d, orderDirection: %d } ) { projectMembers{ user { id, fullName, shortName, email, createdAt }, joinedAt }, search, limit, order, offset, pageCount, currentPage, totalCount } } }",
|
|
createdProject.ID.String(),
|
|
5,
|
|
"",
|
|
1,
|
|
1,
|
|
2)
|
|
|
|
result := testQuery(t, query)
|
|
|
|
data := result.(map[string]interface{})
|
|
project := data[consoleql.ProjectQuery].(map[string]interface{})
|
|
members := project[consoleql.FieldMembers].(map[string]interface{})
|
|
projectMembers := members[consoleql.FieldProjectMembers].([]interface{})
|
|
|
|
assert.Equal(t, 3, len(projectMembers))
|
|
|
|
testUser := func(t *testing.T, actual map[string]interface{}, expected *console.User) {
|
|
assert.Equal(t, expected.Email, actual[consoleql.FieldEmail])
|
|
assert.Equal(t, expected.FullName, actual[consoleql.FieldFullName])
|
|
assert.Equal(t, expected.ShortName, actual[consoleql.FieldShortName])
|
|
|
|
createdAt := time.Time{}
|
|
err := createdAt.UnmarshalText([]byte(actual[consoleql.FieldCreatedAt].(string)))
|
|
|
|
assert.NoError(t, err)
|
|
assert.True(t, expected.CreatedAt.Equal(createdAt))
|
|
}
|
|
|
|
var foundRoot, foundU1, foundU2 bool
|
|
|
|
for _, entry := range projectMembers {
|
|
member := entry.(map[string]interface{})
|
|
user := member[consoleql.UserType].(map[string]interface{})
|
|
|
|
id := user[consoleql.FieldID].(string)
|
|
switch id {
|
|
case rootUser.ID.String():
|
|
foundRoot = true
|
|
testUser(t, user, rootUser)
|
|
case user1.ID.String():
|
|
foundU1 = true
|
|
testUser(t, user, user1)
|
|
case user2.ID.String():
|
|
foundU2 = true
|
|
testUser(t, user, user2)
|
|
}
|
|
}
|
|
|
|
assert.True(t, foundRoot)
|
|
assert.True(t, foundU1)
|
|
assert.True(t, foundU2)
|
|
})
|
|
|
|
keyInfo1, _, err := service.CreateAPIKey(authCtx, createdProject.ID, "key1")
|
|
require.NoError(t, err)
|
|
|
|
keyInfo2, _, err := service.CreateAPIKey(authCtx, createdProject.ID, "key2")
|
|
require.NoError(t, err)
|
|
|
|
t.Run("Project query api keys", func(t *testing.T) {
|
|
query := fmt.Sprintf(
|
|
"query {project(id: \"%s\") {apiKeys( cursor: { limit: %d, search: \"%s\", page: %d, order: %d, orderDirection: %d } ) { apiKeys { id, name, createdAt, projectID }, search, limit, order, offset, pageCount, currentPage, totalCount } } }",
|
|
createdProject.ID.String(),
|
|
5,
|
|
"",
|
|
1,
|
|
1,
|
|
2)
|
|
|
|
result := testQuery(t, query)
|
|
|
|
data := result.(map[string]interface{})
|
|
project := data[consoleql.ProjectQuery].(map[string]interface{})
|
|
keys := project[consoleql.FieldAPIKeys].(map[string]interface{})
|
|
apiKeys := keys[consoleql.FieldAPIKeys].([]interface{})
|
|
|
|
assert.Equal(t, 2, len(apiKeys))
|
|
|
|
testAPIKey := func(t *testing.T, actual map[string]interface{}, expected *console.APIKeyInfo) {
|
|
assert.Equal(t, expected.Name, actual[consoleql.FieldName])
|
|
assert.Equal(t, expected.ProjectID.String(), actual[consoleql.FieldProjectID])
|
|
|
|
createdAt := time.Time{}
|
|
err := createdAt.UnmarshalText([]byte(actual[consoleql.FieldCreatedAt].(string)))
|
|
|
|
assert.NoError(t, err)
|
|
assert.True(t, expected.CreatedAt.Equal(createdAt))
|
|
}
|
|
|
|
var foundKey1, foundKey2 bool
|
|
|
|
for _, entry := range apiKeys {
|
|
key := entry.(map[string]interface{})
|
|
|
|
id := key[consoleql.FieldID].(string)
|
|
switch id {
|
|
case keyInfo1.ID.String():
|
|
foundKey1 = true
|
|
testAPIKey(t, key, keyInfo1)
|
|
case keyInfo2.ID.String():
|
|
foundKey2 = true
|
|
testAPIKey(t, key, keyInfo2)
|
|
}
|
|
}
|
|
|
|
assert.True(t, foundKey1)
|
|
assert.True(t, foundKey2)
|
|
})
|
|
|
|
project2, err := service.CreateProject(authCtx, console.ProjectInfo{
|
|
Name: "Project2",
|
|
Description: "Test desc",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
t.Run("MyProjects query", func(t *testing.T) {
|
|
query := "query {myProjects{id,name,description,createdAt}}"
|
|
|
|
result := testQuery(t, query)
|
|
|
|
data := result.(map[string]interface{})
|
|
projectsList := data[consoleql.MyProjectsQuery].([]interface{})
|
|
|
|
assert.Equal(t, 2, len(projectsList))
|
|
|
|
testProject := func(t *testing.T, actual map[string]interface{}, expected *console.Project) {
|
|
assert.Equal(t, expected.Name, actual[consoleql.FieldName])
|
|
assert.Equal(t, expected.Description, actual[consoleql.FieldDescription])
|
|
|
|
createdAt := time.Time{}
|
|
err := createdAt.UnmarshalText([]byte(actual[consoleql.FieldCreatedAt].(string)))
|
|
|
|
assert.NoError(t, err)
|
|
assert.True(t, expected.CreatedAt.Equal(createdAt))
|
|
}
|
|
|
|
var foundProj1, foundProj2 bool
|
|
|
|
for _, entry := range projectsList {
|
|
project := entry.(map[string]interface{})
|
|
|
|
id := project[consoleql.FieldID].(string)
|
|
switch id {
|
|
case createdProject.ID.String():
|
|
foundProj1 = true
|
|
testProject(t, project, createdProject)
|
|
case project2.ID.String():
|
|
foundProj2 = true
|
|
testProject(t, project, project2)
|
|
}
|
|
}
|
|
|
|
assert.True(t, foundProj1)
|
|
assert.True(t, foundProj2)
|
|
})
|
|
t.Run("OwnedProjects query", func(t *testing.T) {
|
|
query := fmt.Sprintf(
|
|
"query {ownedProjects( cursor: { limit: %d, page: %d } ) {projects{id, name, ownerId, description, createdAt, memberCount}, limit, offset, pageCount, currentPage, totalCount } }",
|
|
5,
|
|
1,
|
|
)
|
|
|
|
result := testQuery(t, query)
|
|
|
|
data := result.(map[string]interface{})
|
|
projectsPage := data[consoleql.OwnedProjectsQuery].(map[string]interface{})
|
|
|
|
projectsList := projectsPage[consoleql.FieldProjects].([]interface{})
|
|
assert.Len(t, projectsList, 2)
|
|
|
|
assert.EqualValues(t, 1, projectsPage[consoleql.FieldCurrentPage])
|
|
assert.EqualValues(t, 0, projectsPage[consoleql.OffsetArg])
|
|
assert.EqualValues(t, 5, projectsPage[consoleql.LimitArg])
|
|
assert.EqualValues(t, 1, projectsPage[consoleql.FieldPageCount])
|
|
assert.EqualValues(t, 2, projectsPage[consoleql.FieldTotalCount])
|
|
|
|
testProject := func(t *testing.T, actual map[string]interface{}, expected *console.Project, expectedNumMembers int) {
|
|
assert.Equal(t, expected.Name, actual[consoleql.FieldName])
|
|
assert.Equal(t, expected.Description, actual[consoleql.FieldDescription])
|
|
|
|
createdAt := time.Time{}
|
|
err := createdAt.UnmarshalText([]byte(actual[consoleql.FieldCreatedAt].(string)))
|
|
|
|
assert.NoError(t, err)
|
|
assert.True(t, expected.CreatedAt.Equal(createdAt))
|
|
|
|
assert.EqualValues(t, expectedNumMembers, actual[consoleql.FieldMemberCount])
|
|
}
|
|
|
|
var foundProj1, foundProj2 bool
|
|
|
|
for _, entry := range projectsList {
|
|
project := entry.(map[string]interface{})
|
|
|
|
id := project[consoleql.FieldID].(string)
|
|
switch id {
|
|
case createdProject.ID.String():
|
|
foundProj1 = true
|
|
testProject(t, project, createdProject, 3)
|
|
case project2.ID.String():
|
|
foundProj2 = true
|
|
testProject(t, project, project2, 1)
|
|
}
|
|
}
|
|
|
|
assert.True(t, foundProj1)
|
|
assert.True(t, foundProj2)
|
|
})
|
|
})
|
|
}
|