diff --git a/auth/client/client_test.go b/auth/client/client_test.go index eb8db4f0a1..7de2e36316 100644 --- a/auth/client/client_test.go +++ b/auth/client/client_test.go @@ -51,6 +51,7 @@ var _ = Describe("Client", func() { Expect(config).ToNot(BeNil()) Expect(config.Config).ToNot(BeNil()) config.Config.Address = testHttp.NewAddress() + config.ExternalConfig.PathPrefix = "auth" config.Config.UserAgent = testHttp.NewUserAgent() config.Config.ServiceSecret = authTest.NewServiceSecret() config.ExternalConfig.Address = testHttp.NewAddress() @@ -112,6 +113,7 @@ var _ = Describe("Client", func() { Expect(config).ToNot(BeNil()) Expect(config.Config).ToNot(BeNil()) config.Config.Address = server.URL() + config.ExternalConfig.PathPrefix = "auth" config.Config.UserAgent = testHttp.NewUserAgent() config.Config.ServiceSecret = authTest.NewServiceSecret() config.ExternalConfig.Address = server.URL() diff --git a/auth/client/external.go b/auth/client/external.go index 54db9ab2c8..33a592f368 100644 --- a/auth/client/external.go +++ b/auth/client/external.go @@ -60,6 +60,8 @@ type ExternalConfig struct { *platform.Config ServerSessionTokenSecret string ServerSessionTokenTimeout time.Duration + // PathPrefix is the prefix to include in all calls to the external service, if any. + PathPrefix string } func NewExternalConfig() *ExternalConfig { @@ -83,6 +85,7 @@ func (e *ExternalConfig) Load(configReporter config.Reporter) error { } e.ServerSessionTokenTimeout = time.Duration(serverSessionTokenTimeoutInteger) * time.Second } + e.PathPrefix = configReporter.GetWithDefault("path_prefix", "auth") return nil } @@ -110,6 +113,7 @@ type External struct { serverSessionTokenTimeout time.Duration serverSessionTokenMutex sync.Mutex serverSessionTokenSafe string + pathPrefix string closingChannel chan chan bool } @@ -139,6 +143,7 @@ func NewExternal(cfg *ExternalConfig, authorizeAs platform.AuthorizeAs, name str name: name, serverSessionTokenSecret: cfg.ServerSessionTokenSecret, serverSessionTokenTimeout: cfg.ServerSessionTokenTimeout, + pathPrefix: cfg.PathPrefix, }, nil } @@ -204,7 +209,7 @@ func (e *External) ValidateSessionToken(ctx context.Context, token string) (requ IsServer bool UserID string } - if err := e.client.RequestData(ctx, "GET", e.client.ConstructURL("auth", "token", token), nil, nil, &result); err != nil { + if err := e.client.RequestData(ctx, "GET", e.client.ConstructURL(e.pathPrefix, "token", token), nil, nil, &result); err != nil { return nil, err } @@ -305,7 +310,7 @@ func (e *External) refreshServerSessionToken() error { e.logger.Debug("Refreshing server session token") requestMethod := "POST" - requestURL := e.client.ConstructURL("auth", "serverlogin") + requestURL := e.client.ConstructURL(e.pathPrefix, "serverlogin") request, err := http.NewRequest(requestMethod, requestURL, nil) if err != nil { return errors.Wrapf(err, "unable to create new request for %s %s", requestMethod, requestURL) diff --git a/auth/service/service/service.go b/auth/service/service/service.go index a95afa3939..d8cf5991b9 100644 --- a/auth/service/service/service.go +++ b/auth/service/service/service.go @@ -108,7 +108,7 @@ func (s *Service) Initialize(provider application.Provider) error { if err := s.initializeDeviceCheck(); err != nil { return err } - return s.initializeUserEventsHandler() + return s.initializeUserEventsHandler(provider) } func (s *Service) Terminate() { @@ -394,13 +394,20 @@ func (s *Service) terminateAuthClient() { } } -func (s *Service) initializeUserEventsHandler() error { +func (s *Service) initializeUserEventsHandler(provider application.Provider) error { s.Logger().Debug("Initializing user events handler") - ctx := logInternal.NewContextWithLogger(context.Background(), s.Logger()) - handler := authEvents.NewUserDataDeletionHandler(ctx, s.authClient) - handlers := []eventsCommon.EventHandler{handler} - runner := events.NewRunner(handlers) + var runner events.Runner + + configReporter := provider.ConfigReporter().WithScopes("user", "events", "handler") + if configReporter.GetWithDefault("disable", "") != "true" { + ctx := logInternal.NewContextWithLogger(context.Background(), s.Logger()) + handler := authEvents.NewUserDataDeletionHandler(ctx, s.authClient) + handlers := []eventsCommon.EventHandler{handler} + runner = events.NewRunner(handlers) + } else { + runner = events.NewNoopRunner() + } if err := runner.Initialize(); err != nil { return errors.Wrap(err, "unable to initialize events runner") diff --git a/blob/service/service.go b/blob/service/service.go index a0ab187878..56ca37a1e4 100644 --- a/blob/service/service.go +++ b/blob/service/service.go @@ -65,7 +65,7 @@ func (s *Service) Initialize(provider application.Provider) error { if err := s.initializeBlobClient(); err != nil { return err } - if err := s.initializeUserEventsHandler(); err != nil { + if err := s.initializeUserEventsHandler(provider); err != nil { return err } return s.initializeRouter() @@ -174,13 +174,20 @@ func (s *Service) terminateBlobUnstructuredStore() { } } -func (s *Service) initializeUserEventsHandler() error { +func (s *Service) initializeUserEventsHandler(provider application.Provider) error { s.Logger().Debug("Initializing user events handler") - ctx := logInternal.NewContextWithLogger(context.Background(), s.Logger()) - handler := blobEvents.NewUserDataDeletionHandler(ctx, s.blobClient) - handlers := []eventsCommon.EventHandler{handler} - runner := events.NewRunner(handlers) + var runner events.Runner + + configReporter := provider.ConfigReporter().WithScopes("user", "events", "handler") + if configReporter.GetWithDefault("disable", "") != "true" { + ctx := logInternal.NewContextWithLogger(context.Background(), s.Logger()) + handler := blobEvents.NewUserDataDeletionHandler(ctx, s.blobClient) + handlers := []eventsCommon.EventHandler{handler} + runner = events.NewRunner(handlers) + } else { + runner = events.NewNoopRunner() + } if err := runner.Initialize(); err != nil { return errors.Wrap(err, "unable to initialize events runner") diff --git a/client/client.go b/client/client.go index eb46822506..7802ff56a0 100644 --- a/client/client.go +++ b/client/client.go @@ -40,7 +40,11 @@ func New(cfg *Config) (*Client, error) { func (c *Client) ConstructURL(paths ...string) string { segments := []string{} for _, path := range paths { - segments = append(segments, url.PathEscape(strings.Trim(path, "/"))) + escapedPath := url.PathEscape(strings.Trim(path, "/")) + if escapedPath == "" { + continue + } + segments = append(segments, escapedPath) } return fmt.Sprintf("%s/%s", strings.TrimRight(c.address, "/"), strings.Join(segments, "/")) } diff --git a/client/config.go b/client/config.go index d2cb931432..81b3f1f023 100644 --- a/client/config.go +++ b/client/config.go @@ -1,6 +1,7 @@ package client import ( + "fmt" "net/url" "time" @@ -33,7 +34,7 @@ func (c *Config) Validate() error { if c.Address == "" { return errors.New("address is missing") } else if _, err := url.Parse(c.Address); err != nil { - return errors.New("address is invalid") + return fmt.Errorf("address is invalid: %w", err) } if c.UserAgent == "" { return errors.New("user agent is missing") diff --git a/client/config_test.go b/client/config_test.go index 4597902c8c..ede7fc4fce 100644 --- a/client/config_test.go +++ b/client/config_test.go @@ -88,7 +88,7 @@ var _ = Describe("Config", func() { It("returns an error if the address is not a parseable URL", func() { cfg.Address = "Not%Parseable" - Expect(cfg.Validate()).To(MatchError("address is invalid")) + Expect(cfg.Validate()).To(MatchError("address is invalid: parse \"Not%Parseable\": invalid URL escape \"%Pa\"")) }) It("returns an error if the user agent is missing", func() { diff --git a/data/service/service/standard.go b/data/service/service/standard.go index e8997a702c..42adbec1c6 100644 --- a/data/service/service/standard.go +++ b/data/service/service/standard.go @@ -81,7 +81,7 @@ func (s *Standard) Initialize(provider application.Provider) error { if err := s.initializeDataSourceClient(); err != nil { return err } - if err := s.initializeUserEventsHandler(); err != nil { + if err := s.initializeUserEventsHandler(provider); err != nil { return err } if err := s.initializeAPI(); err != nil { @@ -382,14 +382,22 @@ func (s *Standard) initializeServer() error { return nil } -func (s *Standard) initializeUserEventsHandler() error { +func (s *Standard) initializeUserEventsHandler(provider application.Provider) error { s.Logger().Debug("Initializing user events handler") sarama.Logger = log.New(os.Stdout, "SARAMA ", log.LstdFlags|log.Lshortfile) - ctx := logInternal.NewContextWithLogger(context.Background(), s.Logger()) - handler := dataEvents.NewUserDataDeletionHandler(ctx, s.dataStore, s.dataSourceStructuredStore) - handlers := []eventsCommon.EventHandler{handler} - runner := events.NewRunner(handlers) + var runner events.Runner + + configReporter := provider.ConfigReporter().WithScopes("user", "events", "handler") + if configReporter.GetWithDefault("disable", "") != "true" { + ctx := logInternal.NewContextWithLogger(context.Background(), s.Logger()) + handler := dataEvents.NewUserDataDeletionHandler(ctx, s.dataStore, s.dataSourceStructuredStore) + handlers := []eventsCommon.EventHandler{handler} + runner = events.NewRunner(handlers) + } else { + runner = events.NewNoopRunner() + } + if err := runner.Initialize(); err != nil { return errors.Wrap(err, "unable to initialize user events handler runner") } diff --git a/env.sh b/env.sh index 83e56e7f82..dad312de34 100644 --- a/env.sh +++ b/env.sh @@ -30,6 +30,7 @@ export TIDEPOOL_PERMISSION_CLIENT_ADDRESS="http://localhost:8009" export TIDEPOOL_TASK_CLIENT_ADDRESS="http://localhost:8009" export TIDEPOOL_USER_CLIENT_ADDRESS="http://localhost:8009" +export TIDEPOOL_AUTH_CLIENT_EXTERNAL_PATH_PREFIX="auth" export TIDEPOOL_AUTH_CLIENT_EXTERNAL_ADDRESS="http://localhost:8009" export TIDEPOOL_AUTH_CLIENT_EXTERNAL_SERVER_SESSION_TOKEN_SECRET="This needs to be the same secret everywhere. YaHut75NsK1f9UKUXuWqxNN0RUwHFBCy" diff --git a/events/events.go b/events/events.go index e1d3129300..240850c756 100644 --- a/events/events.go +++ b/events/events.go @@ -47,3 +47,28 @@ func (r *runner) Terminate() error { } return nil } + +type noopRunner struct { + terminate chan struct{} +} + +func (n *noopRunner) Initialize() error { + n.terminate = make(chan struct{}, 0) + return nil +} + +func (n *noopRunner) Run() error { + <-n.terminate + return nil +} + +func (n *noopRunner) Terminate() error { + n.terminate <- struct{}{} + return nil +} + +var _ Runner = &noopRunner{} + +func NewNoopRunner() Runner { + return &noopRunner{} +} diff --git a/platform/config_test.go b/platform/config_test.go index 98b82c817b..8ecd5796e4 100644 --- a/platform/config_test.go +++ b/platform/config_test.go @@ -112,7 +112,7 @@ var _ = Describe("Config", func() { It("returns an error if the address is not a parseable URL", func() { cfg.Address = "Not%Parseable" - Expect(cfg.Validate()).To(MatchError("address is invalid")) + Expect(cfg.Validate()).To(MatchError("address is invalid: parse \"Not%Parseable\": invalid URL escape \"%Pa\"")) }) It("returns an error if the user agent is missing", func() { diff --git a/store/structured/mongo/config.go b/store/structured/mongo/config.go index 72bdf08031..8212f760d1 100644 --- a/store/structured/mongo/config.go +++ b/store/structured/mongo/config.go @@ -28,15 +28,16 @@ func LoadConfig() (*Config, error) { // Config describe parameters need to make a connection to a Mongo database type Config struct { - Scheme string `json:"scheme" envconfig:"TIDEPOOL_STORE_SCHEME"` - Addresses []string `json:"addresses" envconfig:"TIDEPOOL_STORE_ADDRESSES" required:"true"` - TLS bool `json:"tls" envconfig:"TIDEPOOL_STORE_TLS" default:"true"` - Database string `json:"database" envconfig:"TIDEPOOL_STORE_DATABASE" required:"true"` - CollectionPrefix string `json:"collectionPrefix" envconfig:"TIDEPOOL_STORE_COLLECTION_PREFIX"` - Username *string `json:"-" envconfig:"TIDEPOOL_STORE_USERNAME"` - Password *string `json:"-" envconfig:"TIDEPOOL_STORE_PASSWORD"` - Timeout time.Duration `json:"timeout" envconfig:"TIDEPOOL_STORE_TIMEOUT" default:"60s"` - OptParams *string `json:"optParams" envconfig:"TIDEPOOL_STORE_OPT_PARAMS"` + Scheme string `json:"scheme" envconfig:"TIDEPOOL_STORE_SCHEME"` + Addresses []string `json:"addresses" envconfig:"TIDEPOOL_STORE_ADDRESSES" required:"true"` + TLS bool `json:"tls" envconfig:"TIDEPOOL_STORE_TLS" default:"true"` + Database string `json:"database" envconfig:"TIDEPOOL_STORE_DATABASE" required:"true"` + CollectionPrefix string `json:"collectionPrefix" envconfig:"TIDEPOOL_STORE_COLLECTION_PREFIX"` + Username *string `json:"-" envconfig:"TIDEPOOL_STORE_USERNAME"` + Password *string `json:"-" envconfig:"TIDEPOOL_STORE_PASSWORD"` + Timeout time.Duration `json:"timeout" envconfig:"TIDEPOOL_STORE_TIMEOUT" default:"60s"` + OptParams *string `json:"optParams" envconfig:"TIDEPOOL_STORE_OPT_PARAMS"` + DisableIndexCreation bool `json:"disableIndexCreation" envconfig:"TIDEPOOL_DISABLE_INDEX_CREATION"` } // AsConnectionString constructs a MongoDB connection string from a Config diff --git a/store/structured/mongo/repository.go b/store/structured/mongo/repository.go index 274d9217db..16f2ba79f2 100644 --- a/store/structured/mongo/repository.go +++ b/store/structured/mongo/repository.go @@ -15,15 +15,25 @@ import ( type Repository struct { *mongo.Collection + config RepositoryConfig } -func NewRepository(collection *mongo.Collection) *Repository { +type RepositoryConfig struct { + DisableIndexCreation bool +} + +func NewRepository(collection *mongo.Collection, config RepositoryConfig) *Repository { return &Repository{ collection, + config, } } func (r *Repository) CreateAllIndexes(ctx context.Context, indexes []mongo.IndexModel) error { + if r.config.DisableIndexCreation { + return nil + } + if ctx == nil { ctx = context.Background() } diff --git a/store/structured/mongo/store.go b/store/structured/mongo/store.go index 93ef92d1e6..584373bfc9 100644 --- a/store/structured/mongo/store.go +++ b/store/structured/mongo/store.go @@ -62,7 +62,10 @@ func AppendLifecycleHooksToStore(store *Store, lifecycle fx.Lifecycle) { } func (o *Store) GetRepository(collection string) *Repository { - return NewRepository(o.GetCollection(collection)) + config := RepositoryConfig{ + DisableIndexCreation: o.config.DisableIndexCreation, + } + return NewRepository(o.GetCollection(collection), config) } func (o *Store) GetCollection(collection string) *mongoDriver.Collection {