Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/Integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ permissions:

env:
CARGO_TERM_COLOR: always
SLACK_TEST_WEBHOOK: ${{ secrets.SLACK_TEST_WEBHOOK }}
SLACK_TEST_BOT_TOKEN: ${{ secrets.SLACK_TEST_BOT_TOKEN }}

jobs:
ubuntu-cranelift:
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
/runtime/target
/modules/target
/target
/plaid-stl/target
/plaid-stl/target
20 changes: 19 additions & 1 deletion modules/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions modules/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ resolver = "2"


members = [
"examples/example_github_graphql",

"tests/test_crashtest",
"tests/test_db",
"tests/test_fileopen",
Expand All @@ -17,6 +19,7 @@ members = [
"tests/test_time",
"tests/test_shared_db_rule_1",
"tests/test_shared_db_rule_2",
"tests/test_slack",
]

[profile.release]
Expand Down
13 changes: 13 additions & 0 deletions modules/examples/example_github_graphql/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "example_github_graphql"
description = "Example to show how to use the GitHub GraphQL API with Plaid."
version = "0.1.0"
edition = "2021"

[dependencies]
plaid_stl = { path = "../../../runtime/plaid-stl" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"

[lib]
crate-type = ["cdylib"]
171 changes: 171 additions & 0 deletions modules/examples/example_github_graphql/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
use plaid_stl::{entrypoint_with_source_and_response, github, messages::LogSource, plaid};

use serde::{Deserialize, Serialize};
use serde_json::Value;

use std::{collections::HashMap, error::Error, fmt::Display};

#[derive(Debug)]
enum Errors {
BadSender = 1,
BadConfiguration,
BadAuthentication,
NoOrganization,
NetworkFailure,
UnknownFailure,
}

impl Display for Errors {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}

impl Error for Errors {}

// This is configured in the runtime configuration within the api.toml
// (because it is part of the GitHub API configuration)
const GITHUB_GRAPHQL_QUERY: &str = "saml_identities";

entrypoint_with_source_and_response!();

#[derive(Deserialize)]
struct PageInfo {
#[serde(rename = "hasNextPage")]
has_next_page: bool,
#[serde(rename = "endCursor")]
end_cursor: String,
}

#[derive(Deserialize)]
struct SamlIdentity {
#[serde(rename = "nameId")]
name_id: Option<String>,
}

#[derive(Deserialize)]
struct User {
login: String,
}

#[derive(Deserialize)]
struct Node {
user: Option<User>,
#[serde(rename = "samlIdentity")]
saml_identity: SamlIdentity,
}

#[derive(Deserialize)]
struct ExternalIdentities {
#[serde(rename = "pageInfo")]
page_info: PageInfo,
nodes: Vec<Node>,
}

#[derive(Serialize)]
struct ReturnData {
found_users: HashMap<String, String>,
emails_no_users: Vec<String>,
users_no_emails: Vec<String>,
}

fn simple_const_time_compare(a: &str, b: &str) -> bool {
// Compare lengths first to short-circuit if they are not equal
if a.len() != b.len() {
return false;
}
// Compare each character in constant time
a.chars()
.zip(b.chars())
.fold(0, |acc, (x, y)| acc | (x as u8 ^ y as u8))
== 0
}

fn main(_: String, source: LogSource) -> Result<Option<String>, Errors> {
// This module should only be called from a webhook POST request
match source {
LogSource::WebhookGet(_) => {}
_ => return Err(Errors::BadSender),
};

let true_auth_token = plaid::get_secrets("organization_fetch_auth_token")
.map_err(|_| Errors::BadConfiguration)?;

let provided_auth_token =
plaid::get_headers("Authorization").map_err(|_| Errors::BadAuthentication)?;

if !simple_const_time_compare(&true_auth_token, &provided_auth_token) {
return Err(Errors::BadAuthentication);
}

let organization =
plaid::get_query_params("organization").map_err(|_| Errors::NoOrganization)?;

// Hold all the results as a mapping of user ID to email
let mut found_users: HashMap<String, String> = HashMap::new();
let mut emails_no_users = Vec::new();
let mut users_no_emails = Vec::new();
let mut previous_cursor = String::new();
// We need to make multiple requests because there could be more than 100 users in
// an organization, and the GraphQL API returns a maximum of 100 results.
loop {
let variables = [
("organization".to_string(), organization.clone()),
("cursor".to_string(), previous_cursor.clone()),
]
.into();

match github::make_graphql_query(GITHUB_GRAPHQL_QUERY, variables) {
Ok(result) => {
let result: Value = serde_json::from_str(&result).unwrap();

let ext_ident = result
.get("data")
.and_then(|v| v.get("organization"))
.and_then(|v| v.get("samlIdentityProvider"))
.and_then(|v| v.get("externalIdentities"));

let ext_ident: ExternalIdentities =
serde_json::from_value(ext_ident.unwrap().clone()).map_err(|e| {
plaid::print_debug_string(&format!(
"Failed to deserialize external identities: {e}",
));
Errors::NetworkFailure
})?;

for user in ext_ident.nodes {
match (user.user, user.saml_identity.name_id) {
(Some(u), Some(email)) => {
// We have both a user and an email
found_users.insert(u.login, email);
}
(Some(u), None) => {
// We have a user but no email
users_no_emails.push(u.login);
}
(None, Some(email)) => {
// We have an email but no user
emails_no_users.push(email);
}
(None, None) => {
// Neither user nor email
continue;
}
}
}
if !ext_ident.page_info.has_next_page {
return Ok(Some(
serde_json::to_string(&ReturnData {
found_users,
emails_no_users,
users_no_emails,
})
.unwrap(),
));
}
previous_cursor = ext_ident.page_info.end_cursor;
}
_ => return Err(Errors::UnknownFailure),
}
}
}
66 changes: 33 additions & 33 deletions modules/tests/test_db/harness/harness.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,50 +16,50 @@ RH_PID=$!

sleep 2
# Call the webhook
curl -d "get:some_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "insert:my_key:first_value" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "get:some_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "get:my_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "insert:my_key:second_value" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "get:some_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "get:my_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "delete:my_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "get:my_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "delete:another_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "get:another_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "get:some_key" http://$PLAID_LOCATION/webhook/$URL
curl -d "insert:my_key:first_value" http://$PLAID_LOCATION/webhook/$URL
curl -d "get:some_key" http://$PLAID_LOCATION/webhook/$URL
curl -d "get:my_key" http://$PLAID_LOCATION/webhook/$URL
curl -d "insert:my_key:second_value" http://$PLAID_LOCATION/webhook/$URL
curl -d "get:some_key" http://$PLAID_LOCATION/webhook/$URL
curl -d "get:my_key" http://$PLAID_LOCATION/webhook/$URL
curl -d "delete:my_key" http://$PLAID_LOCATION/webhook/$URL
curl -d "get:my_key" http://$PLAID_LOCATION/webhook/$URL
curl -d "delete:another_key" http://$PLAID_LOCATION/webhook/$URL
curl -d "get:another_key" http://$PLAID_LOCATION/webhook/$URL
# At this point the DB is empty
curl -d "insert:my_key:first_value" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "insert:my_key:first_value" http://$PLAID_LOCATION/webhook/$URL
# too many bytes for the configured storage limit
curl -d "insert:a_key:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "insert:a_key:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" http://$PLAID_LOCATION/webhook/$URL
# empty because insertion failed
curl -d "get:a_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "get:a_key" http://$PLAID_LOCATION/webhook/$URL
# this is within the limit, so it's fine
curl -d "insert:a_key:a" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "insert:a_key:a" http://$PLAID_LOCATION/webhook/$URL
# returns "a"
curl -d "get:a_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "delete:my_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "delete:a_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "get:a_key" http://$PLAID_LOCATION/webhook/$URL
curl -d "delete:my_key" http://$PLAID_LOCATION/webhook/$URL
curl -d "delete:a_key" http://$PLAID_LOCATION/webhook/$URL
# now the DB is empty, so we can insert the long key/value pair
curl -d "insert:a_key:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "get:a_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "delete:a_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "insert:a_key:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" http://$PLAID_LOCATION/webhook/$URL
curl -d "get:a_key" http://$PLAID_LOCATION/webhook/$URL
curl -d "delete:a_key" http://$PLAID_LOCATION/webhook/$URL
# the DB is empty
curl -d "insert:my_key:a" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "insert:my_new_key:b" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "insert:a_key:c" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "list_keys:all:my_key|my_new_key|a_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "list_keys:prefix:my:my_key|my_new_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "insert:my_key:a" http://$PLAID_LOCATION/webhook/$URL
curl -d "insert:my_new_key:b" http://$PLAID_LOCATION/webhook/$URL
curl -d "insert:a_key:c" http://$PLAID_LOCATION/webhook/$URL
curl -d "list_keys:all:my_key|my_new_key|a_key" http://$PLAID_LOCATION/webhook/$URL
curl -d "list_keys:prefix:my:my_key|my_new_key" http://$PLAID_LOCATION/webhook/$URL
curl -d "delete:my_key" http://$PLAID_LOCATION/webhook/$URL
curl -d "delete:my_new_key" http://$PLAID_LOCATION/webhook/$URL
curl -d "delete:a_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "delete:a_key" http://$PLAID_LOCATION/webhook/$URL
# the DB is empty
curl -d "insert:some_key:a" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "insert_check_returned_data:some_key:a" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "delete:some_key" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "insert:some_key:a" http://$PLAID_LOCATION/webhook/$URL
curl -d "insert_check_returned_data:some_key:a" http://$PLAID_LOCATION/webhook/$URL
curl -d "delete:some_key" http://$PLAID_LOCATION/webhook/$URL
# the DB is empty
curl -d "insert:some_key:a" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "delete_check_returned_data:some_key:a" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "insert:some_key:some_value" http://$PLAID_LOCATION/webhook/$URL; sleep 0.5
curl -d "insert:some_key:a" http://$PLAID_LOCATION/webhook/$URL
curl -d "delete_check_returned_data:some_key:a" http://$PLAID_LOCATION/webhook/$URL
curl -d "insert:some_key:some_value" http://$PLAID_LOCATION/webhook/$URL
curl -d "delete_check_returned_data:some_key:some_value" http://$PLAID_LOCATION/webhook/$URL
# the DB is empty

Expand Down
13 changes: 13 additions & 0 deletions modules/tests/test_slack/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "test_slack"
description = "Test rule to check the Slack API works"
version = "0.1.0"
edition = "2021"

[dependencies]
plaid_stl = { path = "../../../runtime/plaid-stl" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"

[lib]
crate-type = ["cdylib"]
41 changes: 41 additions & 0 deletions modules/tests/test_slack/harness/harness.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash
set -e
# If GITHUB_ACTIONS is not set, skip because Plaid won't be running
# with the Slack API properly configured
if [ -z "$GITHUB_ACTIONS" ]; then
echo "Not running in GitHub Actions, skipping Slack tests"
exit 0
fi

if [ -z "$SLACK_TEST_WEBHOOK" ] || [ -z "$SLACK_TEST_BOT_TOKEN" ]; then
echo "Slack secrets are not available, skipping Slack tests"
exit 0
fi

URL="testslack"
FILE="received_data.$URL.txt"

# Start the webhook
$REQUEST_HANDLER > $FILE &
if [ $? -ne 0 ]; then
echo "SlackTest: Failed to start request handler"
rm $FILE
exit 1
fi

RH_PID=$!



# Call the webhook
OUTPUT=$(curl -XPOST -d 'slack_input' http://$PLAID_LOCATION/webhook/$URL)
sleep 2
kill $RH_PID 2>&1 > /dev/null

echo -e "OK\nOK\nOK\nOK\nOK" > expected.txt
diff expected.txt $FILE
RESULT=$?

rm -f $FILE expected.txt

exit $RESULT
Loading