github.com/letsencrypt/boulder@v0.20251208.0/sfe/sfe_test.go (about)

     1  package sfe
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"net/url"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/jmhodges/clock"
    13  	"google.golang.org/grpc"
    14  
    15  	"github.com/letsencrypt/boulder/cmd"
    16  	"github.com/letsencrypt/boulder/features"
    17  	blog "github.com/letsencrypt/boulder/log"
    18  	"github.com/letsencrypt/boulder/metrics"
    19  	"github.com/letsencrypt/boulder/mocks"
    20  	"github.com/letsencrypt/boulder/must"
    21  	"github.com/letsencrypt/boulder/ratelimits"
    22  	"github.com/letsencrypt/boulder/test"
    23  	"github.com/letsencrypt/boulder/unpause"
    24  
    25  	rapb "github.com/letsencrypt/boulder/ra/proto"
    26  )
    27  
    28  type MockRegistrationAuthority struct {
    29  	rapb.RegistrationAuthorityClient
    30  }
    31  
    32  func (ra *MockRegistrationAuthority) UnpauseAccount(context.Context, *rapb.UnpauseAccountRequest, ...grpc.CallOption) (*rapb.UnpauseAccountResponse, error) {
    33  	return &rapb.UnpauseAccountResponse{}, nil
    34  }
    35  
    36  func mustParseURL(s string) *url.URL {
    37  	return must.Do(url.Parse(s))
    38  }
    39  
    40  func setupSFE(t *testing.T) (SelfServiceFrontEndImpl, clock.FakeClock) {
    41  	features.Reset()
    42  
    43  	fc := clock.NewFake()
    44  	// Set to some non-zero time.
    45  	fc.Set(time.Date(2020, 10, 10, 0, 0, 0, 0, time.UTC))
    46  
    47  	stats := metrics.NoopRegisterer
    48  	logger := blog.NewMock()
    49  
    50  	mockSA := mocks.NewStorageAuthorityReadOnly(fc)
    51  
    52  	hmacKey := cmd.HMACKeyConfig{KeyFile: "../test/secrets/sfe_unpause_key"}
    53  	key, err := hmacKey.Load()
    54  	test.AssertNotError(t, err, "Unable to load HMAC key")
    55  
    56  	limiter, err := ratelimits.NewLimiter(fc, ratelimits.NewInmemSource(), stats)
    57  	test.AssertNotError(t, err, "making limiter")
    58  	txnBuilder, err := ratelimits.NewTransactionBuilderFromFiles("../test/config-next/sfe-ratelimit-defaults.yml", "", stats, logger)
    59  	test.AssertNotError(t, err, "making transaction composer")
    60  
    61  	sfe, err := NewSelfServiceFrontEndImpl(
    62  		stats,
    63  		fc,
    64  		logger,
    65  		10*time.Second,
    66  		&MockRegistrationAuthority{},
    67  		mockSA,
    68  		nil,
    69  		key,
    70  		nil,
    71  		limiter,
    72  		txnBuilder,
    73  		false,
    74  	)
    75  	test.AssertNotError(t, err, "Unable to create SFE")
    76  
    77  	return sfe, fc
    78  }
    79  
    80  func TestIndexPath(t *testing.T) {
    81  	t.Parallel()
    82  	sfe, _ := setupSFE(t)
    83  	responseWriter := httptest.NewRecorder()
    84  	sfe.Index(responseWriter, &http.Request{
    85  		Method: "GET",
    86  		URL:    mustParseURL("/"),
    87  	})
    88  
    89  	test.AssertEquals(t, responseWriter.Code, http.StatusOK)
    90  	test.AssertContains(t, responseWriter.Body.String(), "<title>Let's Encrypt - Portal</title>")
    91  }
    92  
    93  func TestBuildIDPath(t *testing.T) {
    94  	t.Parallel()
    95  	sfe, _ := setupSFE(t)
    96  	responseWriter := httptest.NewRecorder()
    97  	sfe.BuildID(responseWriter, &http.Request{
    98  		Method: "GET",
    99  		URL:    mustParseURL("/build"),
   100  	})
   101  
   102  	test.AssertEquals(t, responseWriter.Code, http.StatusOK)
   103  	test.AssertContains(t, responseWriter.Body.String(), "Boulder=(")
   104  }
   105  
   106  func TestUnpausePaths(t *testing.T) {
   107  	t.Parallel()
   108  	sfe, fc := setupSFE(t)
   109  	unpauseSigner, err := unpause.NewJWTSigner(cmd.HMACKeyConfig{KeyFile: "../test/secrets/sfe_unpause_key"})
   110  	test.AssertNotError(t, err, "Should have been able to create JWT signer, but could not")
   111  
   112  	// GET with no JWT
   113  	responseWriter := httptest.NewRecorder()
   114  	sfe.UnpauseForm(responseWriter, &http.Request{
   115  		Method: "GET",
   116  		URL:    mustParseURL(unpause.GetForm),
   117  	})
   118  	test.AssertEquals(t, responseWriter.Code, http.StatusOK)
   119  	test.AssertContains(t, responseWriter.Body.String(), "Invalid unpause URL")
   120  
   121  	// GET with an invalid JWT
   122  	responseWriter = httptest.NewRecorder()
   123  	sfe.UnpauseForm(responseWriter, &http.Request{
   124  		Method: "GET",
   125  		URL:    mustParseURL(fmt.Sprintf(unpause.GetForm + "?jwt=x")),
   126  	})
   127  	test.AssertEquals(t, responseWriter.Code, http.StatusOK)
   128  	test.AssertContains(t, responseWriter.Body.String(), "Invalid unpause URL")
   129  
   130  	// GET with an expired JWT
   131  	expiredJWT, err := unpause.GenerateJWT(unpauseSigner, 1234567890, []string{"example.net"}, time.Hour, fc)
   132  	test.AssertNotError(t, err, "Should have been able to create JWT, but could not")
   133  	responseWriter = httptest.NewRecorder()
   134  	// Advance the clock by 337 hours to make the JWT expired.
   135  	fc.Add(time.Hour * 337)
   136  	sfe.UnpauseForm(responseWriter, &http.Request{
   137  		Method: "GET",
   138  		URL:    mustParseURL(unpause.GetForm + "?jwt=" + expiredJWT),
   139  	})
   140  	test.AssertEquals(t, responseWriter.Code, http.StatusOK)
   141  	test.AssertContains(t, responseWriter.Body.String(), "Expired unpause URL")
   142  
   143  	// GET with a valid JWT and a single identifier
   144  	validJWT, err := unpause.GenerateJWT(unpauseSigner, 1234567890, []string{"example.com"}, time.Hour, fc)
   145  	test.AssertNotError(t, err, "Should have been able to create JWT, but could not")
   146  	responseWriter = httptest.NewRecorder()
   147  	sfe.UnpauseForm(responseWriter, &http.Request{
   148  		Method: "GET",
   149  		URL:    mustParseURL(unpause.GetForm + "?jwt=" + validJWT),
   150  	})
   151  	test.AssertEquals(t, responseWriter.Code, http.StatusOK)
   152  	test.AssertContains(t, responseWriter.Body.String(), "Action required to unpause your account")
   153  	test.AssertContains(t, responseWriter.Body.String(), "example.com")
   154  
   155  	// GET with a valid JWT and multiple identifiers
   156  	validJWT, err = unpause.GenerateJWT(unpauseSigner, 1234567890, []string{"example.com", "example.net", "example.org"}, time.Hour, fc)
   157  	test.AssertNotError(t, err, "Should have been able to create JWT, but could not")
   158  	responseWriter = httptest.NewRecorder()
   159  	sfe.UnpauseForm(responseWriter, &http.Request{
   160  		Method: "GET",
   161  		URL:    mustParseURL(unpause.GetForm + "?jwt=" + validJWT),
   162  	})
   163  	test.AssertEquals(t, responseWriter.Code, http.StatusOK)
   164  	test.AssertContains(t, responseWriter.Body.String(), "Action required to unpause your account")
   165  	test.AssertContains(t, responseWriter.Body.String(), "example.com")
   166  	test.AssertContains(t, responseWriter.Body.String(), "example.net")
   167  	test.AssertContains(t, responseWriter.Body.String(), "example.org")
   168  
   169  	// POST with an expired JWT
   170  	responseWriter = httptest.NewRecorder()
   171  	sfe.UnpauseSubmit(responseWriter, &http.Request{
   172  		Method: "POST",
   173  		URL:    mustParseURL(unpausePostForm + "?jwt=" + expiredJWT),
   174  	})
   175  	test.AssertEquals(t, responseWriter.Code, http.StatusOK)
   176  	test.AssertContains(t, responseWriter.Body.String(), "Expired unpause URL")
   177  
   178  	// POST with no JWT
   179  	responseWriter = httptest.NewRecorder()
   180  	sfe.UnpauseSubmit(responseWriter, &http.Request{
   181  		Method: "POST",
   182  		URL:    mustParseURL(unpausePostForm),
   183  	})
   184  	test.AssertEquals(t, responseWriter.Code, http.StatusOK)
   185  	test.AssertContains(t, responseWriter.Body.String(), "Invalid unpause URL")
   186  
   187  	// POST with an invalid JWT, missing one of the three parts
   188  	responseWriter = httptest.NewRecorder()
   189  	sfe.UnpauseSubmit(responseWriter, &http.Request{
   190  		Method: "POST",
   191  		URL:    mustParseURL(unpausePostForm + "?jwt=x.x"),
   192  	})
   193  	test.AssertEquals(t, responseWriter.Code, http.StatusOK)
   194  	test.AssertContains(t, responseWriter.Body.String(), "Invalid unpause URL")
   195  
   196  	// POST with an invalid JWT, all parts present but missing some characters
   197  	responseWriter = httptest.NewRecorder()
   198  	sfe.UnpauseSubmit(responseWriter, &http.Request{
   199  		Method: "POST",
   200  		URL:    mustParseURL(unpausePostForm + "?jwt=x.x.x"),
   201  	})
   202  	test.AssertEquals(t, responseWriter.Code, http.StatusOK)
   203  	test.AssertContains(t, responseWriter.Body.String(), "Invalid unpause URL")
   204  
   205  	// POST with a valid JWT redirects to a success page
   206  	responseWriter = httptest.NewRecorder()
   207  	sfe.UnpauseSubmit(responseWriter, &http.Request{
   208  		Method: "POST",
   209  		URL:    mustParseURL(unpausePostForm + "?jwt=" + validJWT),
   210  	})
   211  	test.AssertEquals(t, responseWriter.Code, http.StatusFound)
   212  	test.AssertEquals(t, unpauseStatus+"?count=0", responseWriter.Result().Header.Get("Location"))
   213  
   214  	// Redirecting after a successful unpause POST displays the success page.
   215  	responseWriter = httptest.NewRecorder()
   216  	sfe.UnpauseStatus(responseWriter, &http.Request{
   217  		Method: "GET",
   218  		URL:    mustParseURL(unpauseStatus + "?count=1"),
   219  	})
   220  	test.AssertEquals(t, responseWriter.Code, http.StatusOK)
   221  	test.AssertContains(t, responseWriter.Body.String(), "Successfully unpaused all 1 identifier(s)")
   222  
   223  	// Redirecting after a successful unpause POST with a count of 0 displays
   224  	// the already unpaused page.
   225  	responseWriter = httptest.NewRecorder()
   226  	sfe.UnpauseStatus(responseWriter, &http.Request{
   227  		Method: "GET",
   228  		URL:    mustParseURL(unpauseStatus + "?count=0"),
   229  	})
   230  	test.AssertEquals(t, responseWriter.Code, http.StatusOK)
   231  	test.AssertContains(t, responseWriter.Body.String(), "Account already unpaused")
   232  
   233  	// Redirecting after a successful unpause POST with a count equal to the
   234  	// maximum number of identifiers displays the success with caveat page.
   235  	responseWriter = httptest.NewRecorder()
   236  	sfe.UnpauseStatus(responseWriter, &http.Request{
   237  		Method: "GET",
   238  		URL:    mustParseURL(unpauseStatus + "?count=" + fmt.Sprintf("%d", unpause.RequestLimit)),
   239  	})
   240  	test.AssertEquals(t, responseWriter.Code, http.StatusOK)
   241  	test.AssertContains(t, responseWriter.Body.String(), "Some identifiers were unpaused")
   242  }