server: limit length of event trigger names (close #5786) (#5826)

https://github.com/hasura/graphql-engine/pull/5826
This commit is contained in:
Sameer Kolhar 2020-10-06 20:52:09 +05:30 committed by GitHub
parent 4062cdcbea
commit af32ccff36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 41 additions and 27 deletions

View File

@ -58,6 +58,8 @@ This release contains the [PDV refactor (#4111)](https://github.com/hasura/graph
- server: change `created_at` column type from `timestamp` to `timestamptz` for scheduled triggers tables (fix #5722)
- server: allow configuring timeouts for actions (fixes #4966)
- server: accept only non-negative integers for batch size and refetch interval (close #5653) (#5759)
- server: limit the length of event trigger names (close #5786)
**NOTE:** If you have event triggers with names greater than 42 chars, then you should update their names to avoid running into Postgres identifier limit bug (#5786)
- console: allow user to cascade Postgres dependencies when dropping Postgres objects (close #5109) (#5248)
- console: mark inconsistent remote schemas in the UI (close #5093) (#5181)
- console: remove ONLY as default for ALTER TABLE in column alter operations (close #5512) #5706

View File

@ -198,6 +198,7 @@ const Add: React.FC<Props> = props => {
className={`${styles.tableNameInput} form-control`}
value={name}
onChange={handleTriggerNameChange}
maxLength={42}
/>
<hr />
<h4 className={styles.subheading_text}>

View File

@ -9,7 +9,8 @@ export const manualTriggerInfo = (
export const triggerNameDescription = (
<Tooltip id="tooltip-trigger-name-description">
Trigger name can be alphanumeric and can contain underscores and hyphens
Trigger name can be alphanumeric, can contain underscores and hyphens, and
must be at most 42 characters.
</Tooltip>
);

View File

@ -514,7 +514,7 @@ processScheduledEvent logEnv pgpool se@ScheduledEventFull {..} type' = Tracing.r
(processSuccess pgpool se decodedHeaders type' webhookReqBodyJson)
res
where
traceNote = "Scheduled trigger" <> foldMap ((": " <>) . unNonEmptyText . unTriggerName) sefName
traceNote = "Scheduled trigger" <> foldMap ((": " <>) . triggerNameToTxt) sefName
processError
:: (MonadIO m, MonadError QErr m)

View File

@ -39,6 +39,8 @@ import qualified Text.Shakespeare.Text as ST
data OpVar = OLD | NEW deriving (Show)
-- pgIdenTrigger is a method used to construct the name of the pg function
-- used for event triggers which are present in the hdb_views schema.
pgIdenTrigger:: Ops -> TriggerName -> T.Text
pgIdenTrigger op trn = pgFmtIden . qualifyTriggerName op $ triggerNameToTxt trn
where

View File

@ -30,7 +30,7 @@ import Data.Aeson.TH
import Hasura.Incremental (Cacheable)
import Hasura.Prelude
import Hasura.RQL.DDL.Headers
import Hasura.RQL.Types.Common (NonEmptyText (..), InputWebhook)
import Hasura.RQL.Types.Common (NonEmptyText(..), InputWebhook)
import Hasura.SQL.Types
import Language.Haskell.TH.Syntax (Lift)
@ -39,9 +39,17 @@ import qualified Data.Text as T
import qualified Database.PG.Query as Q
import qualified Text.Regex.TDFA as TDFA
-- This change helps us create functions for the event triggers
-- without the function name being truncated by PG, since PG allows
-- for only 63 chars for identifiers.
-- Reasoning for the 42 characters:
-- 63 - (notify_hasura_) - (_INSERT | _UPDATE | _DELETE)
maxTriggerNameLength :: Int
maxTriggerNameLength = 42
-- | Unique name for event trigger.
newtype TriggerName = TriggerName { unTriggerName :: NonEmptyText }
deriving (Show, Eq, Hashable, Lift, DQuote, FromJSON, ToJSON, ToJSONKey, Q.FromCol, Q.ToPrepArg, Generic, Arbitrary, NFData, Cacheable)
deriving (Show, Eq, Hashable, Lift, DQuote, FromJSON, ToJSON, ToJSONKey, Q.ToPrepArg, Generic, NFData, Cacheable, Arbitrary, Q.FromCol)
triggerNameToTxt :: TriggerName -> Text
triggerNameToTxt = unNonEmptyText . unTriggerName
@ -116,7 +124,7 @@ instance ToJSON WebhookConf where
instance FromJSON WebhookConf where
parseJSON (Object o) = WCEnv <$> o .: "from_env"
parseJSON t@(String _) =
case (fromJSON t) of
case fromJSON t of
Error s -> fail s
Success a -> pure $ WCValue a
parseJSON _ = fail "one of string or object must be provided for webhook"
@ -146,25 +154,25 @@ data CreateEventTriggerQuery
instance FromJSON CreateEventTriggerQuery where
parseJSON (Object o) = do
name <- o .: "name"
table <- o .: "table"
insert <- o .:? "insert"
update <- o .:? "update"
delete <- o .:? "delete"
enableManual <- o .:? "enable_manual" .!= False
retryConf <- o .:? "retry_conf"
webhook <- o .:? "webhook"
webhookFromEnv <- o .:? "webhook_from_env"
headers <- o .:? "headers"
replace <- o .:? "replace" .!= False
name <- o .: "name"
table <- o .: "table"
insert <- o .:? "insert"
update <- o .:? "update"
delete <- o .:? "delete"
enableManual <- o .:? "enable_manual" .!= False
retryConf <- o .:? "retry_conf"
webhook <- o .:? "webhook"
webhookFromEnv <- o .:? "webhook_from_env"
headers <- o .:? "headers"
replace <- o .:? "replace" .!= False
let regex = "^[A-Za-z]+[A-Za-z0-9_\\-]*$" :: LBS.ByteString
compiledRegex = TDFA.makeRegex regex :: TDFA.Regex
isMatch = TDFA.match compiledRegex . T.unpack $ triggerNameToTxt name
if isMatch then return ()
else fail "only alphanumeric and underscore and hyphens allowed for name"
if any isJust [insert, update, delete] || enableManual then
return ()
else
unless isMatch $
fail "only alphanumeric and underscore and hyphens allowed for name"
unless (T.length (triggerNameToTxt name) <= maxTriggerNameLength) $
fail "event trigger name can be at most 42 characters"
unless (any isJust [insert, update, delete] || enableManual) $
fail "atleast one amongst insert/update/delete/enable_manual spec must be provided"
case (webhook, webhookFromEnv) of
(Just _, Nothing) -> return ()
@ -195,10 +203,8 @@ instance NFData TriggerOpsDef
instance Cacheable TriggerOpsDef
$(deriveJSON (aesonDrop 2 snakeCase){omitNothingFields=True} ''TriggerOpsDef)
data DeleteEventTriggerQuery
= DeleteEventTriggerQuery
{ detqName :: !TriggerName
} deriving (Show, Eq, Lift)
newtype DeleteEventTriggerQuery = DeleteEventTriggerQuery { detqName :: TriggerName }
deriving (Show, Eq, Lift)
$(deriveJSON (aesonDrop 4 snakeCase){omitNothingFields=True} ''DeleteEventTriggerQuery)

View File

@ -65,7 +65,7 @@ execPGDump b ci = do
-- delete front matter
|| line `elem` preambleLines
-- delete notify triggers
|| notifyTriggerRegex `TDFA.match` line
|| eventTriggerRegex `TDFA.match` line
preambleLines =
[ "SET statement_timeout = 0;"
@ -85,8 +85,10 @@ execPGDump b ci = do
, "COMMENT ON SCHEMA public IS 'standard public schema';"
]
notifyTriggerRegex =
eventTriggerRegex =
let regexStr :: String =
-- pg functions created by hasura for event triggers used "notify_hasura"
-- These changes are also documented on the method pgIdenTrigger
"^CREATE TRIGGER \"?notify_hasura_.+\"? AFTER [[:alnum:]]+ "
<> "ON .+ FOR EACH ROW EXECUTE (FUNCTION|PROCEDURE) "
<> "\"?hdb_views\"?\\.\"?notify_hasura_.+\"?\\(\\);$"