Skip to content

Commit

Permalink
fix: Add Flagsmith signature header when testing webhook. (#3666)
Browse files Browse the repository at this point in the history
Co-authored-by: kyle-ssg <[email protected]>
  • Loading branch information
shubham-padia and kyle-ssg authored Apr 11, 2024
1 parent 2366f78 commit c950875
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 19 deletions.
2 changes: 2 additions & 0 deletions api/core/signing.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@


def sign_payload(payload: str, key: str):
# signPayload of frontend/web/components/TestWebHook on the frontend replicates this
# exact function, change the function there if this changes.
return hmac.new(
key=key.encode(), msg=payload.encode(), digestmod=hashlib.sha256
).hexdigest()
86 changes: 67 additions & 19 deletions frontend/web/components/TestWebhook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,82 @@ import Button from './base/forms/Button'
type TestWebhookType = {
webhook: string
json: string
secret: string
}

const TestWebhook: FC<TestWebhookType> = ({ json, webhook }) => {
// from https://stackoverflow.com/questions/24834812/space-in-between-json-stringify-output
const stringifyWithSpaces = (str: string) => {
const obj = JSON.parse(str)
let result = JSON.stringify(obj, null, 1) // stringify, with line-breaks and indents
result = result.replace(/^ +/gm, ' ') // remove all but the first space for each line
result = result.replace(/\n/g, '') // remove line-breaks
result = result.replace(/{ /g, '{').replace(/ }/g, '}') // remove spaces between object-braces and first/last props
result = result.replace(/\[ /g, '[').replace(/ \]/g, ']') // remove spaces between array-brackets and first/last items
return result
}

const signPayload = async (body: string, secret: string): Promise<string> => {
if(!secret) {
return ''
}
const enc = new TextEncoder()

const key = await window.crypto.subtle.importKey(
'raw',
enc.encode(secret),
{
hash: { name: 'SHA-256' },
name: 'HMAC',
},
false,
['sign'],
)

const signature = await window.crypto.subtle.sign(
'HMAC',
key,
enc.encode(stringifyWithSpaces(body)), // We do this bc the python output is single line with one space before each value
)
const signatureUnsignedIntArray = new Uint8Array(signature)
return Array.prototype.map
.call(signatureUnsignedIntArray, (element) =>
element.toString(16).padStart(2, '0'),
)
.join('')
}

const TestWebhook: FC<TestWebhookType> = ({ json, secret, webhook }) => {
const [error, setError] = useState<string | null>(null)
const [loading, setLoading] = useState(false)
const [success, setSuccess] = useState(false)

const submit = () => {
setError(null)
setLoading(true)
setSuccess(false)
data
.post(webhook, JSON.parse(json), null)
.then(() => {
setLoading(false)
setSuccess(true)
})
.catch((e) => {
if (e.text) {
e.text().then((error: string) => {
setError(`The server returned an error: ${error}`)
})
} else {
setError('There was an error posting to your webhook.')
}
})
.finally(() => {
setLoading(false)
})
signPayload(json, secret).then((sign) => {
const headers = {
'X-Flagsmith-Signature': sign,
}
data
.post(webhook, JSON.parse(json), headers)
.then(() => {
setLoading(false)
setSuccess(true)
})
.catch((e) => {
if (e.text) {
e.text().then((error: string) => {
setError(`The server returned an error: ${error}`)
})
} else {
setError('There was an error posting to your webhook.')
}
})
.finally(() => {
setLoading(false)
})
})
}
return (
<div>
Expand Down
1 change: 1 addition & 0 deletions frontend/web/components/modals/CreateAuditWebhook.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ const CreateAuditWebhook = class extends Component {
<TestWebhook
json={Constants.exampleAuditWebhook}
webhook={this.state.url}
secret={this.state.secret}
/>
{isEdit ? (
<Button
Expand Down
1 change: 1 addition & 0 deletions frontend/web/components/modals/CreateWebhook.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ const CreateWebhook = class extends Component {
<TestWebhook
json={Constants.exampleWebhook}
webhook={this.state.url}
secret={this.state.secret}
/>
{isEdit ? (
<Button
Expand Down

0 comments on commit c950875

Please sign in to comment.