diff --git a/analyze.sh b/analyze.sh new file mode 100755 index 0000000000..1620e9bb73 --- /dev/null +++ b/analyze.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ROOT="/Users/matejbaco/Documents/GitHub/appwrite" +ENDPOINT_DIR="$ROOT/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2" +CONFIG_FILE="$ROOT/app/config/oAuthProviders.php" + +if ! command -v php >/dev/null 2>&1; then + echo "php is required but was not found in PATH" >&2 + exit 1 +fi + +if [[ ! -d "$ENDPOINT_DIR" ]]; then + echo "Endpoint directory not found: $ENDPOINT_DIR" >&2 + exit 1 +fi + +if [[ ! -f "$CONFIG_FILE" ]]; then + echo "Config file not found: $CONFIG_FILE" >&2 + exit 1 +fi + +echo "OAuth2 endpoint files:" +find "$ENDPOINT_DIR" -type f | sort +echo + +tmp_dir="$(mktemp -d)" +trap 'rm -rf "$tmp_dir"' EXIT + +endpoint_file="$tmp_dir/endpoint-providers.txt" +config_file="$tmp_dir/config-providers.txt" + +find "$ENDPOINT_DIR" -mindepth 2 -maxdepth 2 -type f -name 'Update.php' \ + | while read -r file; do + basename "$(dirname "$file")" | tr '[:upper:]' '[:lower:]' + done \ + | sort -u > "$endpoint_file" + +php -r ' + $providers = require $argv[1]; + $names = []; + + foreach ($providers as $provider) { + if (($provider["mock"] ?? false) === true) { + continue; + } + + $class = $provider["class"] ?? ""; + if ($class === "") { + continue; + } + + $base = substr($class, strrpos($class, "\\") + 1); + $names[strtolower($base)] = true; + } + + $names = array_keys($names); + sort($names); + + foreach ($names as $name) { + echo $name, PHP_EOL; + } +' "$CONFIG_FILE" > "$config_file" + +echo "Configured provider classes:" +cat "$config_file" +echo + +echo "Endpoint provider directories:" +cat "$endpoint_file" +echo + +echo "Configured providers without endpoint:" +comm -23 "$config_file" "$endpoint_file" diff --git a/app/init/models.php b/app/init/models.php index da872b5d7b..df0d0d28d8 100644 --- a/app/init/models.php +++ b/app/init/models.php @@ -105,16 +105,34 @@ use Appwrite\Utopia\Response\Model\MigrationReport; use Appwrite\Utopia\Response\Model\Mock; use Appwrite\Utopia\Response\Model\MockNumber; use Appwrite\Utopia\Response\Model\None; +use Appwrite\Utopia\Response\Model\OAuth2Amazon; use Appwrite\Utopia\Response\Model\OAuth2Autodesk; use Appwrite\Utopia\Response\Model\OAuth2Bitbucket; use Appwrite\Utopia\Response\Model\OAuth2Bitly; use Appwrite\Utopia\Response\Model\OAuth2Box; use Appwrite\Utopia\Response\Model\OAuth2Dailymotion; use Appwrite\Utopia\Response\Model\OAuth2Discord; +use Appwrite\Utopia\Response\Model\OAuth2Disqus; use Appwrite\Utopia\Response\Model\OAuth2Dropbox; +use Appwrite\Utopia\Response\Model\OAuth2Etsy; +use Appwrite\Utopia\Response\Model\OAuth2Facebook; use Appwrite\Utopia\Response\Model\OAuth2Figma; use Appwrite\Utopia\Response\Model\OAuth2GitHub; use Appwrite\Utopia\Response\Model\OAuth2Google; +use Appwrite\Utopia\Response\Model\OAuth2Linkedin; +use Appwrite\Utopia\Response\Model\OAuth2Notion; +use Appwrite\Utopia\Response\Model\OAuth2Podio; +use Appwrite\Utopia\Response\Model\OAuth2Salesforce; +use Appwrite\Utopia\Response\Model\OAuth2Slack; +use Appwrite\Utopia\Response\Model\OAuth2Spotify; +use Appwrite\Utopia\Response\Model\OAuth2Stripe; +use Appwrite\Utopia\Response\Model\OAuth2Twitch; +use Appwrite\Utopia\Response\Model\OAuth2WordPress; +use Appwrite\Utopia\Response\Model\OAuth2X; +use Appwrite\Utopia\Response\Model\OAuth2Yahoo; +use Appwrite\Utopia\Response\Model\OAuth2Yandex; +use Appwrite\Utopia\Response\Model\OAuth2Zoho; +use Appwrite\Utopia\Response\Model\OAuth2Zoom; use Appwrite\Utopia\Response\Model\Phone; use Appwrite\Utopia\Response\Model\PlatformAndroid; use Appwrite\Utopia\Response\Model\PlatformApple; @@ -370,6 +388,24 @@ Response::setModel(new OAuth2Bitly()); Response::setModel(new OAuth2Box()); Response::setModel(new OAuth2Autodesk()); Response::setModel(new OAuth2Google()); +Response::setModel(new OAuth2Zoom()); +Response::setModel(new OAuth2Zoho()); +Response::setModel(new OAuth2Yandex()); +Response::setModel(new OAuth2X()); +Response::setModel(new OAuth2WordPress()); +Response::setModel(new OAuth2Twitch()); +Response::setModel(new OAuth2Stripe()); +Response::setModel(new OAuth2Spotify()); +Response::setModel(new OAuth2Slack()); +Response::setModel(new OAuth2Podio()); +Response::setModel(new OAuth2Notion()); +Response::setModel(new OAuth2Salesforce()); +Response::setModel(new OAuth2Yahoo()); +Response::setModel(new OAuth2Linkedin()); +Response::setModel(new OAuth2Disqus()); +Response::setModel(new OAuth2Amazon()); +Response::setModel(new OAuth2Etsy()); +Response::setModel(new OAuth2Facebook()); Response::setModel(new PolicyPasswordDictionary()); Response::setModel(new PolicyPasswordHistory()); Response::setModel(new PolicyPasswordPersonalData()); diff --git a/src/Appwrite/Auth/OAuth2/Discord.php b/src/Appwrite/Auth/OAuth2/Discord.php index 6cb682479a..a5ecdb5e3c 100644 --- a/src/Appwrite/Auth/OAuth2/Discord.php +++ b/src/Appwrite/Auth/OAuth2/Discord.php @@ -1,7 +1,6 @@ addHeader('Accept', 'application/json'); - + $response = $client->fetch( url: 'https://github.com/login/oauth/access_token', method: FetchClient::METHOD_POST, @@ -233,19 +234,19 @@ class Github extends OAuth2 'client_secret' => $this->appSecret, 'code' => 'intentionally-invalid-code', 'redirect_uri' => 'intentionally-invalid-redirect', - ] + ] ); - + $json = \json_decode($response->getBody(), true); - + if (isset($json['error']) && $json['error'] === "Not Found") { throw new \Exception('GitHub application with provided Client ID is does not exist.'); } - + if (isset($json['error']) && $json['error'] === "incorrect_client_credentials") { throw new \Exception('GitHub application with provided Client ID is valid, but the provided Client Secret is incorrect.'); } - + // We still expect error, like redirect_uri_mismatch or bad_verification_code, // but that indicates valid credentials } diff --git a/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Amazon/Update.php b/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Amazon/Update.php new file mode 100644 index 0000000000..b17ce97930 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Amazon/Update.php @@ -0,0 +1,40 @@ +verifyCredentials(); } $oAuthProviders[$enabledKey] = true; - } catch(\Throwable $err) { - if($enabled === true) { + } catch (\Throwable $err) { + if ($enabled === true) { throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Could not enable OAuth2 provider: ' . $err->getMessage()); } } @@ -188,7 +188,7 @@ abstract class Base extends Action 'oAuthProviders' => $oAuthProviders ]); - $project = $authorization->skip(fn() => $dbForPlatform->updateDocument('projects', $project->getId(), $updates)); + $project = $authorization->skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $updates)); $response->dynamic(new Document([ '$id' => $providerId, diff --git a/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Disqus/Update.php b/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Disqus/Update.php new file mode 100644 index 0000000000..978b5c9323 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Project/Http/Project/OAuth2/Disqus/Update.php @@ -0,0 +1,50 @@ +addAction(UpdateAuthMethod::getName(), new UpdateAuthMethod()); - + // OAuth2 $this->addAction(UpdateOAuth2GitHub::getName(), new UpdateOAuth2GitHub()); $this->addAction(UpdateOAuth2Discord::getName(), new UpdateOAuth2Discord()); @@ -151,5 +169,23 @@ class Http extends Service $this->addAction(UpdateOAuth2Box::getName(), new UpdateOAuth2Box()); $this->addAction(UpdateOAuth2Autodesk::getName(), new UpdateOAuth2Autodesk()); $this->addAction(UpdateOAuth2Google::getName(), new UpdateOAuth2Google()); + $this->addAction(UpdateOAuth2Zoom::getName(), new UpdateOAuth2Zoom()); + $this->addAction(UpdateOAuth2Zoho::getName(), new UpdateOAuth2Zoho()); + $this->addAction(UpdateOAuth2Yandex::getName(), new UpdateOAuth2Yandex()); + $this->addAction(UpdateOAuth2X::getName(), new UpdateOAuth2X()); + $this->addAction(UpdateOAuth2WordPress::getName(), new UpdateOAuth2WordPress()); + $this->addAction(UpdateOAuth2Twitch::getName(), new UpdateOAuth2Twitch()); + $this->addAction(UpdateOAuth2Stripe::getName(), new UpdateOAuth2Stripe()); + $this->addAction(UpdateOAuth2Spotify::getName(), new UpdateOAuth2Spotify()); + $this->addAction(UpdateOAuth2Slack::getName(), new UpdateOAuth2Slack()); + $this->addAction(UpdateOAuth2Podio::getName(), new UpdateOAuth2Podio()); + $this->addAction(UpdateOAuth2Notion::getName(), new UpdateOAuth2Notion()); + $this->addAction(UpdateOAuth2Salesforce::getName(), new UpdateOAuth2Salesforce()); + $this->addAction(UpdateOAuth2Yahoo::getName(), new UpdateOAuth2Yahoo()); + $this->addAction(UpdateOAuth2Linkedin::getName(), new UpdateOAuth2Linkedin()); + $this->addAction(UpdateOAuth2Disqus::getName(), new UpdateOAuth2Disqus()); + $this->addAction(UpdateOAuth2Amazon::getName(), new UpdateOAuth2Amazon()); + $this->addAction(UpdateOAuth2Etsy::getName(), new UpdateOAuth2Etsy()); + $this->addAction(UpdateOAuth2Facebook::getName(), new UpdateOAuth2Facebook()); } } diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index dc315d83fd..d005872845 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -288,6 +288,24 @@ class Response extends SwooleResponse public const MODEL_OAUTH2_BOX = 'oAuth2Box'; public const MODEL_OAUTH2_AUTODESK = 'oAuth2Autodesk'; public const MODEL_OAUTH2_GOOGLE = 'oAuth2Google'; + public const MODEL_OAUTH2_ZOOM = 'oAuth2Zoom'; + public const MODEL_OAUTH2_ZOHO = 'oAuth2Zoho'; + public const MODEL_OAUTH2_YANDEX = 'oAuth2Yandex'; + public const MODEL_OAUTH2_X = 'oAuth2X'; + public const MODEL_OAUTH2_WORDPRESS = 'oAuth2WordPress'; + public const MODEL_OAUTH2_TWITCH = 'oAuth2Twitch'; + public const MODEL_OAUTH2_STRIPE = 'oAuth2Stripe'; + public const MODEL_OAUTH2_SPOTIFY = 'oAuth2Spotify'; + public const MODEL_OAUTH2_SLACK = 'oAuth2Slack'; + public const MODEL_OAUTH2_PODIO = 'oAuth2Podio'; + public const MODEL_OAUTH2_NOTION = 'oAuth2Notion'; + public const MODEL_OAUTH2_SALESFORCE = 'oAuth2Salesforce'; + public const MODEL_OAUTH2_YAHOO = 'oAuth2Yahoo'; + public const MODEL_OAUTH2_LINKEDIN = 'oAuth2Linkedin'; + public const MODEL_OAUTH2_DISQUS = 'oAuth2Disqus'; + public const MODEL_OAUTH2_AMAZON = 'oAuth2Amazon'; + public const MODEL_OAUTH2_ETSY = 'oAuth2Etsy'; + public const MODEL_OAUTH2_FACEBOOK = 'oAuth2Facebook'; // Health public const MODEL_HEALTH_STATUS = 'healthStatus'; diff --git a/src/Appwrite/Utopia/Response/Model/OAuth2Amazon.php b/src/Appwrite/Utopia/Response/Model/OAuth2Amazon.php new file mode 100644 index 0000000000..33708374cc --- /dev/null +++ b/src/Appwrite/Utopia/Response/Model/OAuth2Amazon.php @@ -0,0 +1,43 @@ +