1// Copyright (C) 2021 Storj Labs, Inc.
2// See LICENSE for copying information.
3
4package consoleweb_test
5
6import (
7	"bytes"
8	"fmt"
9	"net/http"
10	"testing"
11	"time"
12
13	"github.com/stretchr/testify/require"
14	"go.uber.org/zap"
15
16	"storj.io/common/testcontext"
17	"storj.io/storj/private/testplanet"
18	"storj.io/storj/satellite"
19	"storj.io/storj/satellite/console"
20)
21
22func TestActivationRouting(t *testing.T) {
23	testplanet.Run(t, testplanet.Config{
24		SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 0,
25	}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
26		sat := planet.Satellites[0]
27		service := sat.API.Console.Service
28
29		regToken, err := service.CreateRegToken(ctx, 1)
30		require.NoError(t, err)
31
32		user, err := service.CreateUser(ctx, console.CreateUser{
33			FullName: "User",
34			Email:    "u@mail.test",
35			Password: "123a123",
36		}, regToken.Secret)
37		require.NoError(t, err)
38
39		activationToken, err := service.GenerateActivationToken(ctx, user.ID, user.Email)
40		require.NoError(t, err)
41
42		checkActivationRedirect := func(testMsg, redirectURL string, shouldHaveCookie bool) {
43			url := "http://" + sat.API.Console.Listener.Addr().String() + "/activation/?token=" + activationToken
44
45			req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
46			require.NoError(t, err, testMsg)
47
48			result, err := http.DefaultClient.Do(req)
49			require.NoError(t, err, testMsg)
50
51			// cookie should be set on successful activation
52			hasCookie := false
53			for _, c := range result.Cookies() {
54				if c.Name == "_tokenKey" {
55					hasCookie = true
56					break
57				}
58			}
59			require.Equal(t, shouldHaveCookie, hasCookie)
60
61			require.Equal(t, http.StatusTemporaryRedirect, result.StatusCode, testMsg)
62			require.Equal(t, redirectURL, result.Header.Get("Location"), testMsg)
63			require.NoError(t, result.Body.Close(), testMsg)
64		}
65
66		http.DefaultClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
67			return http.ErrUseLastResponse
68		}
69
70		baseURL := "http://" + sat.API.Console.Listener.Addr().String() + "/"
71		loginURL := baseURL + "login"
72
73		// successful activation should set cookie and redirect to home page.
74		checkActivationRedirect("Activation - Fresh Token", baseURL, true)
75		// unsuccessful redirect should not set cookie and redirect to login page.
76		checkActivationRedirect("Activation - Used Token", loginURL+"?activated=false", false)
77	})
78}
79
80func TestUserIDRateLimiter(t *testing.T) {
81	numLimits := 2
82	testplanet.Run(t, testplanet.Config{
83		SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 0,
84		Reconfigure: testplanet.Reconfigure{
85			Satellite: func(log *zap.Logger, index int, config *satellite.Config) {
86				config.Console.RateLimit.NumLimits = numLimits
87			},
88		},
89	}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
90		sat := planet.Satellites[0]
91
92		applyCouponStatus := func(token string) int {
93			urlLink := "http://" + sat.API.Console.Listener.Addr().String() + "/api/v0/payments/coupon/apply"
94
95			req, err := http.NewRequestWithContext(ctx, http.MethodPatch, urlLink, bytes.NewBufferString("PROMO_CODE"))
96			require.NoError(t, err)
97
98			req.AddCookie(&http.Cookie{
99				Name:    "_tokenKey",
100				Path:    "/",
101				Value:   token,
102				Expires: time.Now().AddDate(0, 0, 1),
103			})
104
105			result, err := http.DefaultClient.Do(req)
106			require.NoError(t, err)
107			require.NoError(t, result.Body.Close())
108
109			return result.StatusCode
110		}
111
112		var firstToken string
113		for userNum := 1; userNum <= numLimits+1; userNum++ {
114			t.Run(fmt.Sprintf("TestUserIDRateLimit_%d", userNum), func(t *testing.T) {
115				user, err := sat.AddUser(ctx, console.CreateUser{
116					FullName: fmt.Sprintf("Test User %d", userNum),
117					Email:    fmt.Sprintf("test%d@mail.test", userNum),
118				}, 1)
119				require.NoError(t, err)
120
121				// sat.AddUser sets password to full name.
122				token, err := sat.API.Console.Service.Token(ctx, console.AuthUser{Email: user.Email, Password: user.FullName})
123				require.NoError(t, err)
124
125				if userNum == 1 {
126					firstToken = token
127				}
128
129				// Expect burst number of successes.
130				for burstNum := 0; burstNum < sat.Config.Console.RateLimit.Burst; burstNum++ {
131					require.NotEqual(t, http.StatusTooManyRequests, applyCouponStatus(token))
132				}
133
134				// Expect failure.
135				require.Equal(t, http.StatusTooManyRequests, applyCouponStatus(token))
136			})
137		}
138
139		// Expect original user to work again because numLimits == 2.
140		for burstNum := 0; burstNum < sat.Config.Console.RateLimit.Burst; burstNum++ {
141			require.NotEqual(t, http.StatusTooManyRequests, applyCouponStatus(firstToken))
142		}
143		require.Equal(t, http.StatusTooManyRequests, applyCouponStatus(firstToken))
144	})
145}
146