hunk.code_window: 		return
   	}
   }
   func (c *Context) RemoteClusterTokenRequired() {
   	if license := c.App.Channels().License(); license == nil || !*license.Features.RemoteClusterService || c.AppContext.Session().Props[model.SessionPropType] != model.SessionTypeRemoteclusterToken {
   		c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "TokenRequired", http.StatusUnauthorized)
   		return
   	}
  
hunk.labels: keep keep keep keep keep replace keep keep keep
hunk.after_edit: 	if license := c.App.Channels().License(); license == nil || !license.HasRemoteClusterService() || c.AppContext.Session().Props[model.SessionPropType] != model.SessionTypeRemoteclusterToken {
  
hunk.file_path: web/context.go
hunk.type: replace
file: // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
  // See LICENSE.txt for license information.
  
  package filestore
  
  import (
  	"bytes"
  	"context"
  	"crypto/rand"
  	"encoding/base64"
  	"errors"
  	"fmt"
  	"net/http/httptest"
  	"net/http/httputil"
  	"net/url"
  	"os"
  	"strings"
  	"testing"
  	"time"
  
  	s3 "github.com/minio/minio-go/v7"
  	"github.com/stretchr/testify/require"
  )
  
  // Copied from model/config.go to avoid an import cycle
  const (
  	MinioAccessKey = "minioaccesskey"
  	MinioSecretKey = "miniosecretkey"
  	ImageDriverS3  = "amazons3"
  )
  
  func TestCheckMandatoryS3Fields(t *testing.T) {
  	cfg := FileBackendSettings{}
  
  	err := cfg.CheckMandatoryS3Fields()
  	require.Error(t, err)
  	require.Equal(t, err.Error(), "missing s3 bucket settings", "should've failed with missing s3 bucket")
  
  	cfg.AmazonS3Bucket = "test-mm"
  	err = cfg.CheckMandatoryS3Fields()
  	require.NoError(t, err)
  
  	cfg.AmazonS3Endpoint = ""
  	err = cfg.CheckMandatoryS3Fields()
  	require.NoError(t, err)
  
  	require.Equal(t, "s3.amazonaws.com", cfg.AmazonS3Endpoint, "should've set the endpoint to the default")
  }
  
  func TestMakeBucket(t *testing.T) {
  	s3Host := os.Getenv("CI_MINIO_HOST")
  	if s3Host == "" {
  		s3Host = "localhost"
  	}
  
  	s3Port := os.Getenv("CI_MINIO_PORT")
  	if s3Port == "" {
  		s3Port = "9000"
  	}
  
  	s3Endpoint := fmt.Sprintf("%s:%s", s3Host, s3Port)
  
  	// Generate a random bucket name
  	b := make([]byte, 30)
  	rand.Read(b)
  	bucketName := base64.StdEncoding.EncodeToString(b)
  	bucketName = strings.ToLower(bucketName)
  	bucketName = strings.Replace(bucketName, "+", "", -1)
  	bucketName = strings.Replace(bucketName, "/", "", -1)
  
  	cfg := FileBackendSettings{
  		DriverName:                         ImageDriverS3,
  		AmazonS3AccessKeyId:                MinioAccessKey,
  		AmazonS3SecretAccessKey:            MinioSecretKey,
  		AmazonS3Bucket:                     bucketName,
  		AmazonS3Endpoint:                   s3Endpoint,
  		AmazonS3Region:                     "",
  		AmazonS3PathPrefix:                 "",
  		AmazonS3SSL:                        false,
  		SkipVerify:                         false,
  		AmazonS3RequestTimeoutMilliseconds: 5000,
  	}
  
  	fileBackend, err := NewS3FileBackend(cfg)
  	require.NoError(t, err)
  
  	err = fileBackend.MakeBucket()
  	require.NoError(t, err)
  }
  
  func TestTimeout(t *testing.T) {
  	s3Host := os.Getenv("CI_MINIO_HOST")
  	if s3Host == "" {
  		s3Host = "localhost"
  	}
  
  	s3Port := os.Getenv("CI_MINIO_PORT")
  	if s3Port == "" {
  		s3Port = "9000"
  	}
  
  	s3Endpoint := fmt.Sprintf("%s:%s", s3Host, s3Port)
  
  	// Generate a random bucket name
  	b := make([]byte, 30)
  	rand.Read(b)
  	bucketName := base64.StdEncoding.EncodeToString(b)
  	bucketName = strings.ToLower(bucketName)
  	bucketName = strings.Replace(bucketName, "+", "", -1)
  	bucketName = strings.Replace(bucketName, "/", "", -1)
  
  	cfg := FileBackendSettings{
  		DriverName:                         ImageDriverS3,
  		AmazonS3AccessKeyId:                MinioAccessKey,
  		AmazonS3SecretAccessKey:            MinioSecretKey,
  		AmazonS3Bucket:                     bucketName,
  		AmazonS3Endpoint:                   s3Endpoint,
  		AmazonS3Region:                     "",
  		AmazonS3PathPrefix:                 "",
  		AmazonS3SSL:                        false,
  		SkipVerify:                         false,
  		AmazonS3RequestTimeoutMilliseconds: 0,
  	}
  
  	fileBackend, err := NewS3FileBackend(cfg)
  	require.NoError(t, err)
  
  	err = fileBackend.MakeBucket()
  	require.True(t, errors.Is(err, context.DeadlineExceeded))
  
  	path := "tests/" + randomString() + ".png"
  	_, err = fileBackend.WriteFile(bytes.NewReader([]byte("testimage")), path)
  	require.True(t, errors.Is(err, context.DeadlineExceeded))
  }
  
  func TestInsecureMakeBucket(t *testing.T) {
  	s3Host := os.Getenv("CI_MINIO_HOST")
  	if s3Host == "" {
  		s3Host = "localhost"
  	}
  
  	s3Port := os.Getenv("CI_MINIO_PORT")
  	if s3Port == "" {
  		s3Port = "9000"
  	}
  
  	s3Endpoint := fmt.Sprintf("%s:%s", s3Host, s3Port)
  
  	proxySelfSignedHTTPS := newTLSProxyServer(&url.URL{Scheme: "http", Host: s3Endpoint})
  	defer proxySelfSignedHTTPS.Close()
  
  	enableInsecure, secure := true, false
  
  	testCases := []struct {
  		description     string
  		skipVerify      bool
  		expectedAllowed bool
  	}{
  		{"allow self-signed HTTPS when insecure enabled", enableInsecure, true},
  		{"reject self-signed HTTPS when secured", secure, false},
  	}
  
  	for _, testCase := range testCases {
  		t.Run(testCase.description, func(t *testing.T) {
  			// Generate a random bucket name
  			b := make([]byte, 30)
  			rand.Read(b)
  			bucketName := base64.StdEncoding.EncodeToString(b)
  			bucketName = strings.ToLower(bucketName)
  			bucketName = strings.Replace(bucketName, "+", "", -1)
  			bucketName = strings.Replace(bucketName, "/", "", -1)
  
  			cfg := FileBackendSettings{
  				DriverName:                         ImageDriverS3,
  				AmazonS3AccessKeyId:                MinioAccessKey,
  				AmazonS3SecretAccessKey:            MinioSecretKey,
  				AmazonS3Bucket:                     bucketName,
  				AmazonS3Endpoint:                   proxySelfSignedHTTPS.URL[8:],
  				AmazonS3Region:                     "",
  				AmazonS3PathPrefix:                 "",
  				AmazonS3SSL:                        true,
  				SkipVerify:                         testCase.skipVerify,
  				AmazonS3RequestTimeoutMilliseconds: 5000,
  			}
  
  			fileBackend, err := NewS3FileBackend(cfg)
  			require.NoError(t, err)
  
  			err = fileBackend.MakeBucket()
  			if testCase.expectedAllowed {
  				require.NoError(t, err)
  			} else {
  				require.Error(t, err)
  			}
  		})
  	}
  }
  func newTLSProxyServer(backend *url.URL) *httptest.Server {
  	return httptest.NewTLSServer(httputil.NewSingleHostReverseProxy(backend))
  }
  
  func TestS3WithCancel(t *testing.T) {
  	// Some of these tests use time.Sleep to wait for the timeout to expire.
  	// They are run in parallel to reduce wait times.
  
  	t.Run("zero timeout", func(t *testing.T) {
  		t.Parallel()
  		r, ctx := newMockS3WithCancel(0, nil)
  
  		time.Sleep(10 * time.Millisecond) // give the context time to cancel
  
  		require.False(t, r.CancelTimeout())
  		require.Error(t, ctx.Err())
  	})
  
  	t.Run("timeout", func(t *testing.T) {
  		t.Parallel()
  		r, ctx := newMockS3WithCancel(50*time.Millisecond, nil)
  
  		time.Sleep(100 * time.Millisecond) // give the context time to cancel
  
  		require.False(t, r.CancelTimeout())
  		require.Error(t, ctx.Err())
  	})
  
  	t.Run("timeout cancel", func(t *testing.T) {
  		t.Parallel()
  		r, ctx := newMockS3WithCancel(50*time.Millisecond, nil)
  
  		time.Sleep(10 * time.Millisecond) // give the context time to cancel
  
  		require.True(t, r.CancelTimeout())
  		require.NoError(t, ctx.Err())
  
  		time.Sleep(100 * time.Millisecond) // wait for the original (canceled) timeout to expire
  
  		require.False(t, r.CancelTimeout())
  		require.NoError(t, ctx.Err())
  		require.NoError(t, r.Close())
  	})
  
  	t.Run("timeout closed", func(t *testing.T) {
  		t.Parallel()
  		r, ctx := newMockS3WithCancel(50*time.Millisecond, nil)
  
  		time.Sleep(10 * time.Millisecond) // give the context time to cancel
  
  		require.True(t, r.CancelTimeout())
  		require.NoError(t, ctx.Err())
  		require.NoError(t, r.Close())
  
  		time.Sleep(100 * time.Millisecond) // wait for the original (canceled) timeout to expire
  
  		require.False(t, r.CancelTimeout())
  		require.Error(t, ctx.Err())
  		require.NoError(t, r.Close())
  	})
  
  	t.Run("close cancel close", func(t *testing.T) {
  		t.Parallel()
  		r, ctx := newMockS3WithCancel(50*time.Millisecond, nil)
  
  		time.Sleep(10 * time.Millisecond) // give the context time to cancel
  
  		require.True(t, r.CancelTimeout())
  		require.NoError(t, r.Close())
  		require.Error(t, ctx.Err())
  		require.False(t, r.CancelTimeout())
  		require.Error(t, ctx.Err())
  		require.NoError(t, r.Close())
  	})
  
  	t.Run("close error", func(t *testing.T) {
  		t.Parallel()
  		r, ctx := newMockS3WithCancel(50*time.Millisecond, errors.New("test error"))
  
  		time.Sleep(10 * time.Millisecond) // give the context time to cancel
  
  		require.NoError(t, ctx.Err())
  		require.Error(t, r.Close())
  		require.False(t, r.CancelTimeout())
  		require.Error(t, ctx.Err())
  	})
  }
  
  func newMockS3WithCancel(timeout time.Duration, closeErr error) (*fauxCloser, context.Context) {
  	ctx, cancel := context.WithCancel(context.Background())
  	return &fauxCloser{
  		s3WithCancel: &s3WithCancel{
  			Object: &s3.Object{},
  			timer:  time.AfterFunc(timeout, cancel),
  			cancel: cancel,
  		},
  		closeErr: closeErr,
  	}, ctx
  }
  
  type fauxCloser struct {
  	*s3WithCancel
  	closeErr error
  }
  
  func (fc fauxCloser) Close() error {
  	fc.s3WithCancel.timer.Stop()
  	fc.s3WithCancel.cancel()
  	return fc.closeErr
  }
  