///|
pub struct ProviderRequestMetadata {
provider : String
event_type : String
event_id : String
timestamp : Int
nonce : String
signature_present : Bool
} derive(Eq, Debug)
///|
pub fn ProviderRequestMetadata::new(
provider : String,
event_type : String,
event_id : String,
timestamp : Int,
nonce : String,
signature_present : Bool,
) -> ProviderRequestMetadata {
{ provider, event_type, event_id, timestamp, nonce, signature_present }
}
///|
fn header_or_empty(request : WebhookRequest, name : String) -> String {
if name == "" {
""
} else {
request.header(name).unwrap_or("")
}
}
///|
pub fn provider_timestamp(
request : WebhookRequest,
profile : ProviderProfile,
) -> Int {
match profile.name {
"stripe" =>
parse_stripe_signature(request.signature(profile.signature_header)).timestamp
"github" => 0
_ => parse_positive_int(header_or_empty(request, profile.timestamp_header))
}
}
///|
pub fn provider_timestamp_text(
request : WebhookRequest,
profile : ProviderProfile,
) -> String {
match profile.name {
"stripe" =>
parse_stripe_signature(request.signature(profile.signature_header)).timestamp_text
"github" => ""
_ => header_or_empty(request, profile.timestamp_header)
}
}
///|
fn stripe_event_type(request : WebhookRequest) -> String {
payload_path_text(request.body, ["type"]).unwrap_or("")
}
///|
fn stripe_event_id(request : WebhookRequest) -> String {
payload_path_text(request.body, ["id"]).unwrap_or("")
}
///|
fn feishu_event_type(
request : WebhookRequest,
profile : ProviderProfile,
) -> String {
payload_first_path(request.body, [
["header", "event_type"],
["event_type"],
["type"],
]).unwrap_or(header_or_empty(request, profile.event_type_header))
}
///|
fn feishu_event_id(
request : WebhookRequest,
profile : ProviderProfile,
) -> String {
payload_first_path(request.body, [
["header", "event_id"],
["event_id"],
["uuid"],
]).unwrap_or(header_or_empty(request, profile.event_id_header))
}
///|
pub fn provider_event_type(
request : WebhookRequest,
profile : ProviderProfile,
) -> String {
match profile.name {
"stripe" => stripe_event_type(request)
"feishu" => feishu_event_type(request, profile)
_ => header_or_empty(request, profile.event_type_header)
}
}
///|
pub fn provider_event_id(
request : WebhookRequest,
profile : ProviderProfile,
) -> String {
match profile.name {
"stripe" => stripe_event_id(request)
"feishu" => feishu_event_id(request, profile)
_ => header_or_empty(request, profile.event_id_header)
}
}
///|
pub fn provider_nonce(
request : WebhookRequest,
profile : ProviderProfile,
) -> String {
match profile.name {
"feishu" => request.header("x-lark-request-nonce").unwrap_or("")
"stripe" => provider_timestamp_text(request, profile)
_ => provider_event_id(request, profile)
}
}
///|
pub fn provider_metadata(
request : WebhookRequest,
profile : ProviderProfile,
) -> ProviderRequestMetadata {
ProviderRequestMetadata::new(
profile.name,
provider_event_type(request, profile),
provider_event_id(request, profile),
provider_timestamp(request, profile),
provider_nonce(request, profile),
request.signature(profile.signature_header) != "",
)
}
///|
pub fn provider_signature_algorithm(profile : ProviderProfile) -> String {
match profile.name {
"github" => "HMAC-SHA256(raw-body)"
"stripe" => "HMAC-SHA256(timestamp.raw-body)"
"feishu" => "SHA256(timestamp+nonce+secret+raw-body)"
_ => "HMAC-SHA256(canonical-request)"
}
}
///|
pub fn provider_metadata_text(metadata : ProviderRequestMetadata) -> String {
let builder = StringBuilder::new()
builder.write_string("provider=")
builder.write_string(metadata.provider)
builder.write_string(" event=")
builder.write_string(metadata.event_type)
builder.write_string(" id=")
builder.write_string(metadata.event_id)
builder.write_string(" timestamp=")
builder.write_string(metadata.timestamp.to_string())
builder.write_string(" nonce=")
if metadata.nonce == "" {
builder.write_string("missing")
} else {
builder.write_string("present")
}
builder.write_string(" signature=")
builder.write_string(
if metadata.signature_present {
"present"
} else {
"missing"
},
)
builder.to_string()
}
///|
pub fn provider_metadata_is_complete(
metadata : ProviderRequestMetadata,
require_timestamp : Bool,
require_event_id : Bool,
) -> Bool {
metadata.signature_present &&
(!require_timestamp || metadata.timestamp > 0) &&
(!require_event_id || metadata.event_id != "")
}