diff --git a/.env.example b/.env.example index 86671be..d95d212 100644 --- a/.env.example +++ b/.env.example @@ -22,4 +22,4 @@ LOG_LEVEL= METRICS_ENABLED=true METRICS_HOST="[::]" METRICS_PORT=4000 -SHUTTER_EVENT_REGISTRY_CONTRACT_ADDRESS= \ No newline at end of file +SHUTTER_EVENT_REGISTRY_CONTRACT_ADDRESS= # Event API is auto-disabled when this is empty diff --git a/common/config.go b/common/config.go index b723967..a6db96a 100644 --- a/common/config.go +++ b/common/config.go @@ -9,10 +9,11 @@ import ( ) type Config struct { - KeyperHTTPURL *url.URL - SigningKey *ecdsa.PrivateKey - PublicKey *ecdsa.PublicKey - P2P *p2p.Config + KeyperHTTPURL *url.URL + SigningKey *ecdsa.PrivateKey + PublicKey *ecdsa.PublicKey + P2P *p2p.Config + DisableEventAPI bool // true when SHUTTER_EVENT_REGISTRY_CONTRACT_ADDRESS is not configured; if true, event API endpoints are not registered } func NewConfig(keyperHTTPUrl string, signingKey *ecdsa.PrivateKey, p2pConfig *p2p.Config) (*Config, error) { diff --git a/docs/docs.go b/docs/docs.go index 73ac0f6..8e415ef 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -135,6 +135,12 @@ const docTemplate = `{ "schema": { "$ref": "#/definitions/error.Http" } + }, + "501": { + "description": "Event API is disabled on this deployment.", + "schema": { + "$ref": "#/definitions/error.Http" + } } } } @@ -193,6 +199,12 @@ const docTemplate = `{ "schema": { "$ref": "#/definitions/error.Http" } + }, + "501": { + "description": "Event API is disabled on this deployment.", + "schema": { + "$ref": "#/definitions/error.Http" + } } } } @@ -258,6 +270,12 @@ const docTemplate = `{ "schema": { "$ref": "#/definitions/error.Http" } + }, + "501": { + "description": "Event API is disabled on this deployment.", + "schema": { + "$ref": "#/definitions/error.Http" + } } } } @@ -288,8 +306,8 @@ const docTemplate = `{ }, { "type": "string", - "description": "Identity prefix associated with the event identity registration.", - "name": "identityPrefix", + "description": "Identity associated with the event identity registration.", + "name": "identity", "in": "query", "required": true } @@ -324,6 +342,12 @@ const docTemplate = `{ "schema": { "$ref": "#/definitions/error.Http" } + }, + "501": { + "description": "Event API is disabled on this deployment.", + "schema": { + "$ref": "#/definitions/error.Http" + } } } } @@ -378,6 +402,12 @@ const docTemplate = `{ "schema": { "$ref": "#/definitions/error.Http" } + }, + "501": { + "description": "Event API is disabled on this deployment.", + "schema": { + "$ref": "#/definitions/error.Http" + } } } } diff --git a/docs/swagger.json b/docs/swagger.json index ad1638a..c6c92c2 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -126,6 +126,12 @@ "schema": { "$ref": "#/definitions/error.Http" } + }, + "501": { + "description": "Event API is disabled on this deployment.", + "schema": { + "$ref": "#/definitions/error.Http" + } } } } @@ -184,6 +190,12 @@ "schema": { "$ref": "#/definitions/error.Http" } + }, + "501": { + "description": "Event API is disabled on this deployment.", + "schema": { + "$ref": "#/definitions/error.Http" + } } } } @@ -249,6 +261,12 @@ "schema": { "$ref": "#/definitions/error.Http" } + }, + "501": { + "description": "Event API is disabled on this deployment.", + "schema": { + "$ref": "#/definitions/error.Http" + } } } } @@ -279,8 +297,8 @@ }, { "type": "string", - "description": "Identity prefix associated with the event identity registration.", - "name": "identityPrefix", + "description": "Identity associated with the event identity registration.", + "name": "identity", "in": "query", "required": true } @@ -315,6 +333,12 @@ "schema": { "$ref": "#/definitions/error.Http" } + }, + "501": { + "description": "Event API is disabled on this deployment.", + "schema": { + "$ref": "#/definitions/error.Http" + } } } } @@ -369,6 +393,12 @@ "schema": { "$ref": "#/definitions/error.Http" } + }, + "501": { + "description": "Event API is disabled on this deployment.", + "schema": { + "$ref": "#/definitions/error.Http" + } } } } diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 216ece0..396d025 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -227,6 +227,10 @@ paths: description: Internal server error. schema: $ref: '#/definitions/error.Http' + "501": + description: Event API is disabled on this deployment. + schema: + $ref: '#/definitions/error.Http' security: - BearerAuth: [] summary: Allows clients to compile an event trigger definition string. @@ -267,6 +271,10 @@ paths: description: Internal server error. schema: $ref: '#/definitions/error.Http' + "501": + description: Event API is disabled on this deployment. + schema: + $ref: '#/definitions/error.Http' security: - BearerAuth: [] summary: Provides data necessary to allow event-based encryption. @@ -311,6 +319,10 @@ paths: description: Internal server error. schema: $ref: '#/definitions/error.Http' + "501": + description: Event API is disabled on this deployment. + schema: + $ref: '#/definitions/error.Http' security: - BearerAuth: [] summary: Get decryption key. @@ -327,9 +339,9 @@ paths: name: eon required: true type: integer - - description: Identity prefix associated with the event identity registration. + - description: Identity associated with the event identity registration. in: query - name: identityPrefix + name: identity required: true type: string produces: @@ -356,6 +368,10 @@ paths: description: Internal server error. schema: $ref: '#/definitions/error.Http' + "501": + description: Event API is disabled on this deployment. + schema: + $ref: '#/definitions/error.Http' security: - BearerAuth: [] summary: Get event identity registration expiration block number. @@ -393,6 +409,10 @@ paths: description: Internal server error. schema: $ref: '#/definitions/error.Http' + "501": + description: Event API is disabled on this deployment. + schema: + $ref: '#/definitions/error.Http' security: - BearerAuth: [] summary: Allows clients to register an event trigger identity. diff --git a/internal/middleware/event_api_guard.go b/internal/middleware/event_api_guard.go new file mode 100644 index 0000000..d055aaa --- /dev/null +++ b/internal/middleware/event_api_guard.go @@ -0,0 +1,28 @@ +package middleware + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/shutter-network/shutter-api/common" + sherror "github.com/shutter-network/shutter-api/internal/error" +) + +// EventAPIGuard returns 501 Not Implemented when DisableEventAPI is true +// (i.e., when SHUTTER_EVENT_REGISTRY_CONTRACT_ADDRESS is not configured). +// When enabled, requests proceed to the event handlers. +func EventAPIGuard(config *common.Config) gin.HandlerFunc { + return func(ctx *gin.Context) { + if config.DisableEventAPI { + err := sherror.NewHttpError( + "Event API is disabled on this deployment", + "Event API is disabled on this deployment", + http.StatusNotImplemented, + ) + ctx.Error(err) + ctx.Abort() + return + } + ctx.Next() + } +} diff --git a/internal/router/router.go b/internal/router/router.go index ae1fe6f..e7fd6d9 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -44,6 +44,7 @@ func NewRouter( } eventGroup := api.Group("/event") + eventGroup.Use(middleware.EventAPIGuard(config)) { eventGroup.POST("/compile_trigger_definition", cryptoService.CompileEventTriggerDefinition) eventGroup.POST("/register_identity", cryptoService.RegisterEventIdentity) diff --git a/internal/service/crypto.go b/internal/service/crypto.go index 5e0308f..4dbd1fd 100644 --- a/internal/service/crypto.go +++ b/internal/service/crypto.go @@ -54,6 +54,7 @@ func NewCryptoService( // @Failure 404 {object} error.Http "Decryption key not found for the associated identity." // @Failure 429 {object} error.Http "Too many requests. Rate limited." // @Failure 500 {object} error.Http "Internal server error." +// @Failure 501 {object} error.Http "Event API is disabled on this deployment." // @Security BearerAuth // @Router /event/get_decryption_key [get] func (svc *CryptoService) GetEventDecryptionKey(ctx *gin.Context) { @@ -188,6 +189,7 @@ func (svc *CryptoService) GetDataForEncryptionTime(ctx *gin.Context) { // @Failure 400 {object} error.Http "Invalid Get data for encryption request." // @Failure 429 {object} error.Http "Too many requests. Rate limited." // @Failure 500 {object} error.Http "Internal server error." +// @Failure 501 {object} error.Http "Event API is disabled on this deployment." // @Security BearerAuth // @Router /event/get_data_for_encryption [get] func (svc *CryptoService) GetDataForEncryptionEvent(ctx *gin.Context) { @@ -358,6 +360,7 @@ func (svc *CryptoService) DecryptCommitment(ctx *gin.Context) { // @Failure 400 {object} error.Http "Invalid Event Data." // @Failure 429 {object} error.Http "Too many requests. Rate limited." // @Failure 500 {object} error.Http "Internal server error." +// @Failure 501 {object} error.Http "Event API is disabled on this deployment." // @Security BearerAuth // @Router /event/compile_trigger_definition [post] func (svc *CryptoService) CompileEventTriggerDefinition(ctx *gin.Context) { @@ -400,6 +403,7 @@ func CompileEventTriggerDefinition(ctx *gin.Context) { // @Failure 400 {object} error.Http "Invalid Register identity request." // @Failure 429 {object} error.Http "Too many requests. Rate limited." // @Failure 500 {object} error.Http "Internal server error." +// @Failure 501 {object} error.Http "Event API is disabled on this deployment." // @Security BearerAuth // @Router /event/register_identity [post] func (svc *CryptoService) RegisterEventIdentity(ctx *gin.Context) { @@ -440,6 +444,7 @@ func (svc *CryptoService) RegisterEventIdentity(ctx *gin.Context) { // @Failure 404 {object} error.Http "Event identity registration not found." // @Failure 429 {object} error.Http "Too many requests. Rate limited." // @Failure 500 {object} error.Http "Internal server error." +// @Failure 501 {object} error.Http "Event API is disabled on this deployment." // @Security BearerAuth // @Router /event/get_trigger_expiration_block [get] func (svc *CryptoService) GetEventTriggerExpirationBlock(ctx *gin.Context) { diff --git a/main.go b/main.go index 0dfd84c..ed850af 100644 --- a/main.go +++ b/main.go @@ -187,6 +187,13 @@ func main() { log.Err(err).Msg("unable to parse keyper http url") return } + // Disable event API if event registry contract address is invalid or empty or zero address + config.DisableEventAPI = + !common.IsHexAddress(shutterEventRegistryContractAddressStringified) || + shutterEventRegistryContractAddress == (common.Address{}) + if config.DisableEventAPI { + log.Info().Msg("Event API disabled: SHUTTER_EVENT_REGISTRY_CONTRACT_ADDRESS not configured") + } app := router.NewRouter(ctx, db, contract, client, config) watcher := watcher.NewWatcher(config, db) group, deferFn := service.RunBackground(ctx, watcher) diff --git a/tests/integration/init_test.go b/tests/integration/init_test.go index 725a8eb..e61affe 100644 --- a/tests/integration/init_test.go +++ b/tests/integration/init_test.go @@ -80,6 +80,7 @@ func (s *TestShutterService) SetupSuite() { s.config, err = common.NewConfig(keyperHTTPUrl, signingKey, &p2pConfig) s.Require().NoError(err) + s.config.DisableEventAPI = false rpc_url := os.Getenv("RPC_URL") s.ethClient, err = ethclient.Dial(rpc_url)