Compare commits

...
104 Commits
Author SHA1 Message Date
Hemachandar 2b96d60c1e copilot - code quality 2025-12-29 16:01:19 +05:30
Hemachandar 1b70bc812b keep certificate renewal in maintenance worker 2025-12-29 14:51:59 +05:30
HemachandarandGitHub e7e429142f Merge branch '1.8.x' into ser-331 2025-12-29 14:20:20 +05:30
Chirag AggarwalandGitHub a0514d2aac Merge pull request #11032 from appwrite/more-sdk-fixes 2025-12-29 11:52:04 +05:30
Chirag AggarwalandGitHub ac137cee3d Merge pull request #11027 from appwrite/lohanidamodar-patch-2 2025-12-29 11:51:00 +05:30
Chirag Aggarwal 58e091099f fix grammar 2025-12-28 21:17:43 +05:30
Chirag Aggarwal 6410a245eb fix: sdk configuration options 2025-12-28 20:59:46 +05:30
fogelito 670edd845e Merge branch '1.8.x' of https://github.com/appwrite/appwrite into lohanidamodar-patch-2 2025-12-28 17:23:10 +02:00
Chirag AggarwalandGitHub 834c18f9fa Merge pull request #11031 from appwrite/fix-default-namespace-in-sdks 2025-12-28 20:50:35 +05:30
Chirag Aggarwal 77930a0cec fix: default namespace in sdks 2025-12-28 20:46:23 +05:30
fogelito 5d9201466b functions + databases 2025-12-28 16:54:38 +02:00
fogelito d583b5e228 All again 2025-12-28 15:33:00 +02:00
fogelito c463a9a662 Only functions 2025-12-28 15:14:56 +02:00
fogelito bd9f02b689 Only functions 2025-12-28 15:01:29 +02:00
fogelito 472e2c282d skip specific filters 2025-12-28 14:31:46 +02:00
fogelito 7a89779827 revert 2025-12-28 14:17:48 +02:00
fogelito 126de78ce2 skip functions 2025-12-28 14:13:56 +02:00
fogelito e919896eef Merge remote-tracking branch 'origin/lohanidamodar-patch-2' into lohanidamodar-patch-2 2025-12-28 13:49:05 +02:00
fogelito fd3475f945 Merge branch '1.8.x' of https://github.com/appwrite/appwrite into lohanidamodar-patch-2 2025-12-28 13:47:29 +02:00
Damodar Lohani 9c8f565211 use the helper method instead 2025-12-28 10:33:12 +00:00
Damodar Lohani 08f30224b6 Use disable instead of skip agin 2025-12-28 10:26:21 +00:00
Damodar LohaniandGitHub 6d02554f2a Merge pull request #11026 from appwrite/fix-file-permissions
Fix: Document/File set user permission only if not privileged user
2025-12-28 15:43:35 +05:45
Damodar Lohani 61c619f373 remove disables 2025-12-28 09:27:21 +00:00
Damodar Lohani b32dd316e1 Fix: use skip filters instead 2025-12-28 09:09:11 +00:00
Damodar LohaniandGitHub 22042b4749 Merge branch '1.8.x' into fix-file-permissions 2025-12-28 14:23:31 +05:45
Damodar LohaniandGitHub aa17eeb6e3 Disable filters for platform and project databases - Stat resources
- Disable filters for platform and project databases - Stat resources
2025-12-28 14:19:28 +05:45
Shimon NewmanandGitHub 1529357821 Merge pull request #11022 from appwrite/fix-bulk-insert-webhook-validation
add webhook exist validation in bulkTrigger()
2025-12-28 09:31:13 +02:00
Damodar Lohani 3a98361736 add test 2025-12-28 02:02:34 +00:00
Damodar Lohani 0c425dbac3 Fix: assign user permission to files/documents only if not a previleged user 2025-12-28 01:48:23 +00:00
Chirag AggarwalandGitHub 69b861cd87 Merge pull request #11025 from appwrite/fix-specs-generation 2025-12-27 21:45:56 +05:30
Chirag Aggarwal 0599d23cc5 fix: specs generation getPlatforms method 2025-12-27 20:48:33 +05:30
Chirag AggarwalandGitHub a37b4a40d9 Merge pull request #11024 from appwrite/turnoff-warning-logs 2025-12-27 20:21:07 +05:30
Chirag Aggarwal 7e37046959 chore: remove warning logs when skipping ssl certificate generation 2025-12-27 20:19:27 +05:30
shimon f9efdfd98e fix: use nullsafe operator for project attribute retrieval in Action class 2025-12-25 19:07:51 +02:00
shimon d53090647b Merge branch '1.8.x' of github.com:appwrite/appwrite into fix-bulk-insert-webhook-validation 2025-12-25 18:56:30 +02:00
shimon e8b8a8fe52 add webhook exist validation in balkTrigger() 2025-12-25 18:52:57 +02:00
Chirag AggarwalandGitHub 73d3d15ed8 Merge pull request #11017 from appwrite/fix-getscreenshot-use-image-format 2025-12-24 14:42:54 +05:30
Chirag Aggarwal b78be65376 fix: make getScreenshot output param to use ImageFormat enum 2025-12-24 13:39:51 +05:30
Luke B. SilverandGitHub 0e75793bef Merge pull request #11013 from appwrite/fix-tests-jwt
Fix JWT test
2025-12-23 17:23:36 +00:00
Matej Bačo 2f4a362e1b Merge branch '1.8.x' into fix-tests-jwt 2025-12-23 16:31:32 +01:00
Matej Bačo cda843d8f6 Fix JWT test 2025-12-23 16:31:17 +01:00
Matej BačoandGitHub d6a40a35ee Merge pull request #11012 from appwrite/chore-increase-jwt-abuse
Increase JWT abuse limit
2025-12-23 15:48:00 +01:00
Matej Bačo e111b4cc18 Increase JWT abuse limit 2025-12-23 15:42:14 +01:00
Chirag AggarwalandGitHub cdbd80ceda Merge pull request #11010 from appwrite/update-schedule-functions 2025-12-23 17:26:09 +05:30
Hemachandar 58751bbdf1 lint 2025-12-23 16:49:35 +05:30
HemachandarandGitHub 21334ecdff Merge branch '1.8.x' into ser-331 2025-12-23 16:37:22 +05:30
Hemachandar 47ea2893c3 rename task to Interval 2025-12-23 16:32:43 +05:30
Chirag Aggarwal bbeca28026 refactor use env variables for queue and class names 2025-12-23 16:09:42 +05:30
Chirag Aggarwal 9ab9f6d709 chore: update schedule functions task 2025-12-23 15:06:29 +05:30
Chirag AggarwalandGitHub 309fde38f7 Merge pull request #11001 from appwrite/update-sdks-console 2025-12-23 13:48:02 +05:30
Chirag Aggarwal 3310d271bf fix import 2025-12-23 12:35:48 +05:30
Chirag Aggarwal 4e12b6236e Merge branch '1.8.x' into update-sdks-console 2025-12-23 12:34:23 +05:30
Torsten DittmannandGitHub 4df8157d74 Merge pull request #11009 from appwrite/feat-allow-custom-jwt-duration 2025-12-22 21:50:45 +04:00
Torsten Dittmann 37e76e52f6 Make JWT expiry configurable for account JWTs 2025-12-22 21:27:29 +04:00
Torsten Dittmann cd824faf64 Add test for JWT with custom duration 2025-12-22 21:19:50 +04:00
Torsten Dittmann 96b11b02e6 feat: allow custom jwt duration 2025-12-22 21:08:48 +04:00
Chirag Aggarwal 9f24cb4aa3 trigger ci 2025-12-22 12:48:23 +05:30
Chirag Aggarwal 9e4e23ad3f chore: update sdks script console log 2025-12-21 22:03:30 +05:30
Jake BarnbyandGitHub 3e96f9c2f1 Merge pull request #10990 from appwrite/project-queries
Allow queries on projects xlist
2025-12-19 14:39:47 +00:00
Jake BarnbyandGitHub dd9f6ed820 Merge pull request #10996 from appwrite/revert-10872-refactor-auth-single-instance
Revert "Refactor auth single instance"
2025-12-19 14:37:41 +00:00
Darshan f7f1bc8352 Merge remote-tracking branch 'origin/project-queries' into project-queries 2025-12-19 19:36:17 +05:30
Darshan bf50fbeb6c bump: order for easier review. 2025-12-19 19:36:05 +05:30
DarshanandGitHub 1f7bfb3352 Merge branch '1.8.x' into project-queries 2025-12-19 19:33:22 +05:30
Darshan 406181e887 revert: to method call 2025-12-19 19:31:05 +05:30
Jake BarnbyandGitHub a7b243aff9 Merge pull request #10992 from appwrite/update-header-name
Update: Header name.
2025-12-19 13:56:00 +00:00
Jake BarnbyandGitHub dc85d44647 Revert "Refactor auth single instance" 2025-12-20 02:54:50 +13:00
Jake BarnbyandGitHub 71621f4ccf Merge pull request #10995 from appwrite/revert-10987-fix-auth
Revert "Fix auth calls"
2025-12-19 13:54:38 +00:00
Jake BarnbyandGitHub 68deee4a56 Revert "Fix auth calls" 2025-12-20 02:54:21 +13:00
Matej BačoandGitHub a20392d13a Merge pull request #10993 from appwrite/feat-agents-md
Feat: Create AGENTS.md
2025-12-19 14:11:14 +01:00
Darshan e43292052c ci: empty commit 2025-12-19 18:02:07 +05:30
Matej Bačo ee7103c4b0 Typo fix 2025-12-19 13:20:57 +01:00
Matej Bačo 1b1ef80b10 Create AGENTS.md 2025-12-19 13:09:48 +01:00
Darshan 33f90fcf6a re-add headers. 2025-12-19 17:02:55 +05:30
Darshan 2f1eee390b update: header name. 2025-12-19 16:23:22 +05:30
Darshan 35bf13cfab update: variable name :D 2025-12-19 16:19:15 +05:30
Darshan f814fd980b update: address comments. 2025-12-19 16:16:56 +05:30
Darshan 3ddfd9c3b0 update: address comment. 2025-12-19 15:52:19 +05:30
DarshanandGitHub 75d8110ad9 Merge branch '1.8.x' into project-queries 2025-12-19 14:23:25 +05:30
Darshan c958f3f1cb update: allow queries on projects xlist. 2025-12-19 14:13:52 +05:30
Steven NguyenandGitHub 06e3a144a3 Merge pull request #10939 from appwrite/fix-smtp-auth-check
Fix smtp auth check
2025-12-18 17:55:43 -08:00
Steven Nguyen 36519c13a8 Merge branch '1.8.x' into fix-smtp-auth-check 2025-12-18 17:21:03 -08:00
Hemachandar cf90653deb lint 2025-12-18 17:30:08 +05:30
Hemachandar 16fb25ce5a tiny 2025-12-18 17:10:02 +05:30
Hemachandar e78887001a spacing 2025-12-18 16:18:23 +05:30
Hemachandar aa4ecdf138 emit events 2025-12-18 16:09:48 +05:30
Hemachandar 3783378288 add action to payload 2025-12-18 15:14:30 +05:30
Hemachandar 5aff78a619 Merge branch '1.8.x' into ser-331 2025-12-18 14:33:34 +05:30
Hemachandar 62b391ef68 certificate status exception 2025-12-18 13:46:56 +05:30
Hemachandar 6f1d2d094a rename task 2025-12-18 13:00:49 +05:30
Hemachandar 88bd35ce98 periodic task for rule verification & certificate generation 2025-12-18 12:40:24 +05:30
Hemachandar db0dbeb27b simplify 2025-12-17 21:04:15 +05:30
Hemachandar 0e27088e2a change function signature 2025-12-17 20:54:18 +05:30
Hemachandar 70a7deaa38 Refactor certificate generation in worker 2025-12-17 20:46:54 +05:30
Chirag AggarwalandGitHub ddb0d5db26 Merge branch '1.8.x' into fix-smtp-auth-check 2025-12-16 10:51:01 +05:30
Steven NguyenandGitHub d4517384dc fix: update project tests to reflect changes in project count and names 2025-12-12 07:16:49 +00:00
Steven NguyenandGitHub a94ccdb4f5 fix: enhance SMTP authentication by using env vars 2025-12-12 06:02:17 +00:00
Steven NguyenandGitHub 9518764cae Merge branch '1.8.x' into fix-smtp-auth-check 2025-12-12 05:24:33 +00:00
Steven NguyenandGitHub d174233cd6 fix: update SMTP configuration and enhance validation checks 2025-12-12 04:58:59 +00:00
Steven NguyenandGitHub 3d0aa74ffe Merge pull request #10863 from VijaykumarPujar-tech/new_branch
Fix: Implement mandatory authentication check for Custom SMTP configuration
2025-12-11 13:51:27 -08:00
VijaykumarPujar-tech e0a937912c Added Validation check for username and password 2025-12-09 23:46:44 +05:30
VijaykumarPujar-tech 8951a8465c Added the projects.php changes back 2025-12-09 23:30:34 +05:30
VijaykumarPujar-tech 2fa95b52a0 Reverted some added changes 2025-12-09 22:40:51 +05:30
Vijaykumar PujarandGitHub 0f84f21bb4 Merge branch 'main' into new_branch 2025-12-08 20:31:25 +05:30
VijaykumarPujar-tech a14d5c584e Fix: robust SMTP validation and added regression test 2025-12-08 20:22:18 +05:30
242 changed files with 4661 additions and 4098 deletions
+3 -2
View File
@@ -69,8 +69,8 @@ _APP_STORAGE_ANTIVIRUS_PORT=3310
_APP_SMTP_HOST=maildev
_APP_SMTP_PORT=1025
_APP_SMTP_SECURE=
_APP_SMTP_USERNAME=
_APP_SMTP_PASSWORD=
_APP_SMTP_USERNAME=user
_APP_SMTP_PASSWORD=password
_APP_SMS_PROVIDER=sms://username:password@mock
_APP_SMS_FROM=+123456789
_APP_SMS_PROJECTS_DENY_LIST=
@@ -101,6 +101,7 @@ _APP_USAGE_AGGREGATION_INTERVAL=30
_APP_STATS_RESOURCES_INTERVAL=30
_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000
_APP_MAINTENANCE_RETENTION_SCHEDULES=86400
_APP_INTERVAL_DOMAIN_VERIFICATION=60
_APP_USAGE_STATS=enabled
_APP_LOGGING_CONFIG=
_APP_LOGGING_CONFIG_REALTIME=
+73
View File
@@ -0,0 +1,73 @@
# AGENTS.md
Appwrite is an end-to-end backend server for web, mobile, native, and backend apps. This guide provides context and instructions for AI coding agents working on the Appwrite codebase.
## Project Overview
Appwrite is a self-hosted Backend-as-a-Service (BaaS) platform that provides developers with a set of APIs and tools to build secure, scalable applications. The project uses a hybrid monolithic-microservice architecture built with PHP, running on Swoole for high performance.
**Key Technologies:**
- **Backend:** PHP 8.3+, Swoole
- **Libraries:** Utopia PHP
- **Database:** MariaDB, Redis
- **Cache:** Redis
- **Queue:** Redis
- **Containers:** Docker
## Development Commands
```bash
# Run Appwrite
docker compose up -d --force-recreate --build
# Run specific test
docker compose exec appwrite test /usr/src/code/tests/e2e/Services/[ServiceName] --filter=[FunctionName]
# Format code
composer format
```
## Code Style Guidelines
- Follow [PSR-12](https://www.php-fig.org/psr/psr-12/) coding standard
- Use PSR-4 autoloading
- Strict type declarations where applicable
- Comprehensive PHPDoc comments
### Naming Conventions
#### `resourceType` Naming Rule
When a collection has a combination of `resourceType`, `resourceId`, and/or `resourceInternalId`, the value of `resourceType` MUST always be **plural** - for example: `functions`, `sites`, `deployments`.
Examples:
```php
'resourceType' => 'functions'
'resourceType' => 'sites'
'resourceType' => 'deployments'
```
## Security Considerations
### Critical Security Practices
- **Never hardcode credentials** - Use environment variables
- **Rate limiting** - Respect abuse prevention mechanisms
## Dependencies
Avoid introducing new dependencies other than utopia-php.
## Pull Request Guidelines
### Before Submitting
- Run `composer format`
- Update documentation if adding features
- Add/update tests for your changes
- Check that Docker build succeeds
`docs/specs/authentication.drawio.svg`
## Known Issues and Gotchas
- **Hot Reload:** Code changes require container restart in some cases
- **Logging:** There is no central place for logs, so when debugging, ensure to check all possibly relevant containers
+1
View File
@@ -57,6 +57,7 @@ RUN mkdir -p /storage/uploads && \
# Executables
RUN chmod +x /usr/local/bin/doctor && \
chmod +x /usr/local/bin/install && \
chmod +x /usr/local/bin/interval && \
chmod +x /usr/local/bin/maintenance && \
chmod +x /usr/local/bin/migrate && \
chmod +x /usr/local/bin/realtime && \
+10 -18
View File
@@ -41,6 +41,8 @@ Config::setParam('runtimes', (new Runtimes('v5'))->getAll(supported: false));
// require controllers after overwriting runtimes
require_once __DIR__ . '/controllers/general.php';
Authorization::disable();
CLI::setResource('register', fn () => $register);
CLI::setResource('cache', function ($pools) {
@@ -58,13 +60,7 @@ CLI::setResource('pools', function (Registry $register) {
return $register->get('pools');
}, ['register']);
CLI::setResource('authorization', function () {
$authorization = new Authorization();
$authorization->disable();
return $authorization;
}, []);
CLI::setResource('dbForPlatform', function ($pools, $cache, $authorization) {
CLI::setResource('dbForPlatform', function ($pools, $cache) {
$sleep = 3;
$maxAttempts = 5;
$attempts = 0;
@@ -78,7 +74,6 @@ CLI::setResource('dbForPlatform', function ($pools, $cache, $authorization) {
$dbForPlatform = new Database($adapter, $cache);
$dbForPlatform
->setAuthorization($authorization)
->setNamespace('_console')
->setMetadata('host', \gethostname())
->setMetadata('project', 'console');
@@ -104,7 +99,7 @@ CLI::setResource('dbForPlatform', function ($pools, $cache, $authorization) {
}
return $dbForPlatform;
}, ['pools', 'cache', 'authorization']);
}, ['pools', 'cache']);
CLI::setResource('console', function () {
return new Document(Config::getParam('console'));
@@ -115,10 +110,10 @@ CLI::setResource(
fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false
);
CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache, $authorization) {
CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) {
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
return function (Document $project) use ($pools, $dbForPlatform, $cache, $authorization, &$databases) {
return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases) {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
@@ -151,7 +146,6 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
$adapter = new DatabasePool($pools->get($dsn->getHost()));
$database = new Database($adapter, $cache);
$databases[$dsn->getHost()] = $database;
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
@@ -168,18 +162,17 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
}
$database
->setAuthorization($authorization)
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId());
return $database;
};
}, ['pools', 'dbForPlatform', 'cache', 'authorization']);
}, ['pools', 'dbForPlatform', 'cache']);
CLI::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
$database = null;
return function (?Document $project = null) use ($pools, $cache, $database, $authorization) {
return function (?Document $project = null) use ($pools, $cache, $database) {
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant((int)$project->getSequence());
return $database;
@@ -189,7 +182,6 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorizatio
$database = new Database($adapter, $cache);
$database
->setAuthorization($authorization)
->setSharedTables(true)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_TASK)
@@ -202,7 +194,7 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorizatio
return $database;
};
}, ['pools', 'cache', 'authorization']);
}, ['pools', 'cache']);
CLI::setResource('publisher', function (Group $pools) {
return new BrokerPool(publisher: $pools->get('publisher'));
}, ['pools']);
+2
View File
@@ -116,6 +116,7 @@ return [
[
'key' => 'android',
'name' => 'Android',
'namespace' => 'io.appwrite',
'version' => '11.4.0',
'url' => 'https://github.com/appwrite/sdk-for-android',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
@@ -395,6 +396,7 @@ return [
[
'key' => 'kotlin',
'name' => 'Kotlin',
'namespace' => 'io.appwrite',
'version' => '13.1.0',
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
+69 -51
View File
@@ -407,8 +407,8 @@
"cookies": false,
"type": "",
"demo": "account\/create-jwt.md",
"rate-limit": 100,
"rate-time": 3600,
"rate-limit": 120,
"rate-time": 60,
"rate-key": "url:{url},userId:{userId}",
"scope": "account",
"platforms": [
@@ -429,7 +429,23 @@
"Session": [],
"JWT": []
}
]
],
"requestBody": {
"content": {
"application\/json": {
"schema": {
"type": "object",
"properties": {
"duration": {
"type": "integer",
"description": "Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.",
"x-example": 0
}
}
}
}
}
}
}
},
"\/account\/logs": {
@@ -535,7 +551,7 @@
"x-appwrite": {
"method": "updateMFA",
"group": "mfa",
"weight": 306,
"weight": 307,
"cookies": false,
"type": "",
"demo": "account\/update-mfa.md",
@@ -607,7 +623,7 @@
"x-appwrite": {
"method": "createMfaAuthenticator",
"group": "mfa",
"weight": 308,
"weight": 309,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-authenticator.md",
@@ -731,7 +747,7 @@
"x-appwrite": {
"method": "updateMfaAuthenticator",
"group": "mfa",
"weight": 309,
"weight": 310,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-authenticator.md",
@@ -871,7 +887,7 @@
"x-appwrite": {
"method": "deleteMfaAuthenticator",
"group": "mfa",
"weight": 310,
"weight": 311,
"cookies": false,
"type": "",
"demo": "account\/delete-mfa-authenticator.md",
@@ -995,7 +1011,7 @@
"x-appwrite": {
"method": "createMfaChallenge",
"group": "mfa",
"weight": 314,
"weight": 315,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-challenge.md",
@@ -1129,7 +1145,7 @@
"x-appwrite": {
"method": "updateMfaChallenge",
"group": "mfa",
"weight": 315,
"weight": 316,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-challenge.md",
@@ -1267,7 +1283,7 @@
"x-appwrite": {
"method": "listMfaFactors",
"group": "mfa",
"weight": 307,
"weight": 308,
"cookies": false,
"type": "",
"demo": "account\/list-mfa-factors.md",
@@ -1368,7 +1384,7 @@
"x-appwrite": {
"method": "getMfaRecoveryCodes",
"group": "mfa",
"weight": 313,
"weight": 314,
"cookies": false,
"type": "",
"demo": "account\/get-mfa-recovery-codes.md",
@@ -1467,7 +1483,7 @@
"x-appwrite": {
"method": "createMfaRecoveryCodes",
"group": "mfa",
"weight": 311,
"weight": 312,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-recovery-codes.md",
@@ -1566,7 +1582,7 @@
"x-appwrite": {
"method": "updateMfaRecoveryCodes",
"group": "mfa",
"weight": 312,
"weight": 313,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-recovery-codes.md",
@@ -2558,7 +2574,8 @@
"yandex",
"zoho",
"zoom",
"mock"
"mock",
"mock-unverified"
],
"x-enum-name": "OAuthProvider",
"x-enum-keys": []
@@ -3488,7 +3505,8 @@
"yandex",
"zoho",
"zoom",
"mock"
"mock",
"mock-unverified"
],
"x-enum-name": "OAuthProvider",
"x-enum-keys": []
@@ -5825,7 +5843,7 @@
"avif",
"gif"
],
"x-enum-name": null,
"x-enum-name": "ImageFormat",
"x-enum-keys": [],
"default": ""
},
@@ -5858,7 +5876,7 @@
"x-appwrite": {
"method": "listTransactions",
"group": "transactions",
"weight": 380,
"weight": 381,
"cookies": false,
"type": "",
"demo": "databases\/list-transactions.md",
@@ -5925,7 +5943,7 @@
"x-appwrite": {
"method": "createTransaction",
"group": "transactions",
"weight": 376,
"weight": 377,
"cookies": false,
"type": "",
"demo": "databases\/create-transaction.md",
@@ -5995,7 +6013,7 @@
"x-appwrite": {
"method": "getTransaction",
"group": "transactions",
"weight": 377,
"weight": 378,
"cookies": false,
"type": "",
"demo": "databases\/get-transaction.md",
@@ -6059,7 +6077,7 @@
"x-appwrite": {
"method": "updateTransaction",
"group": "transactions",
"weight": 378,
"weight": 379,
"cookies": false,
"type": "",
"demo": "databases\/update-transaction.md",
@@ -6137,7 +6155,7 @@
"x-appwrite": {
"method": "deleteTransaction",
"group": "transactions",
"weight": 379,
"weight": 380,
"cookies": false,
"type": "",
"demo": "databases\/delete-transaction.md",
@@ -6203,7 +6221,7 @@
"x-appwrite": {
"method": "createOperations",
"group": "transactions",
"weight": 381,
"weight": 382,
"cookies": false,
"type": "",
"demo": "databases\/create-operations.md",
@@ -6288,7 +6306,7 @@
"x-appwrite": {
"method": "listDocuments",
"group": "documents",
"weight": 339,
"weight": 340,
"cookies": false,
"type": "",
"demo": "databases\/list-documents.md",
@@ -6400,7 +6418,7 @@
"x-appwrite": {
"method": "createDocument",
"group": "documents",
"weight": 331,
"weight": 332,
"cookies": false,
"type": "",
"demo": "databases\/create-document.md",
@@ -6561,7 +6579,7 @@
"x-appwrite": {
"method": "getDocument",
"group": "documents",
"weight": 332,
"weight": 333,
"cookies": false,
"type": "",
"demo": "databases\/get-document.md",
@@ -6672,7 +6690,7 @@
"x-appwrite": {
"method": "upsertDocument",
"group": "documents",
"weight": 335,
"weight": 336,
"cookies": false,
"type": "",
"demo": "databases\/upsert-document.md",
@@ -6827,7 +6845,7 @@
"x-appwrite": {
"method": "updateDocument",
"group": "documents",
"weight": 333,
"weight": 334,
"cookies": false,
"type": "",
"demo": "databases\/update-document.md",
@@ -6939,7 +6957,7 @@
"x-appwrite": {
"method": "deleteDocument",
"group": "documents",
"weight": 337,
"weight": 338,
"cookies": false,
"type": "",
"demo": "databases\/delete-document.md",
@@ -7046,7 +7064,7 @@
"x-appwrite": {
"method": "decrementDocumentAttribute",
"group": "documents",
"weight": 342,
"weight": 343,
"cookies": false,
"type": "",
"demo": "databases\/decrement-document-attribute.md",
@@ -7173,7 +7191,7 @@
"x-appwrite": {
"method": "incrementDocumentAttribute",
"group": "documents",
"weight": 341,
"weight": 342,
"cookies": false,
"type": "",
"demo": "databases\/increment-document-attribute.md",
@@ -7300,7 +7318,7 @@
"x-appwrite": {
"method": "listExecutions",
"group": "executions",
"weight": 472,
"weight": 473,
"cookies": false,
"type": "",
"demo": "functions\/list-executions.md",
@@ -7387,7 +7405,7 @@
"x-appwrite": {
"method": "createExecution",
"group": "executions",
"weight": 470,
"weight": 471,
"cookies": false,
"type": "",
"demo": "functions\/create-execution.md",
@@ -7505,7 +7523,7 @@
"x-appwrite": {
"method": "getExecution",
"group": "executions",
"weight": 471,
"weight": 472,
"cookies": false,
"type": "",
"demo": "functions\/get-execution.md",
@@ -7580,7 +7598,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 241,
"weight": 242,
"cookies": false,
"type": "graphql",
"demo": "graphql\/query.md",
@@ -7634,7 +7652,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 240,
"weight": 241,
"cookies": false,
"type": "graphql",
"demo": "graphql\/mutation.md",
@@ -8120,7 +8138,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 290,
"weight": 291,
"cookies": false,
"type": "",
"demo": "messaging\/create-subscriber.md",
@@ -8204,7 +8222,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 294,
"weight": 295,
"cookies": false,
"type": "",
"demo": "messaging\/delete-subscriber.md",
@@ -9113,7 +9131,7 @@
"x-appwrite": {
"method": "listTransactions",
"group": "transactions",
"weight": 445,
"weight": 446,
"cookies": false,
"type": "",
"demo": "tablesdb\/list-transactions.md",
@@ -9183,7 +9201,7 @@
"x-appwrite": {
"method": "createTransaction",
"group": "transactions",
"weight": 441,
"weight": 442,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-transaction.md",
@@ -9256,7 +9274,7 @@
"x-appwrite": {
"method": "getTransaction",
"group": "transactions",
"weight": 442,
"weight": 443,
"cookies": false,
"type": "",
"demo": "tablesdb\/get-transaction.md",
@@ -9323,7 +9341,7 @@
"x-appwrite": {
"method": "updateTransaction",
"group": "transactions",
"weight": 443,
"weight": 444,
"cookies": false,
"type": "",
"demo": "tablesdb\/update-transaction.md",
@@ -9404,7 +9422,7 @@
"x-appwrite": {
"method": "deleteTransaction",
"group": "transactions",
"weight": 444,
"weight": 445,
"cookies": false,
"type": "",
"demo": "tablesdb\/delete-transaction.md",
@@ -9473,7 +9491,7 @@
"x-appwrite": {
"method": "createOperations",
"group": "transactions",
"weight": 446,
"weight": 447,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-operations.md",
@@ -9561,7 +9579,7 @@
"x-appwrite": {
"method": "listRows",
"group": "rows",
"weight": 437,
"weight": 438,
"cookies": false,
"type": "",
"demo": "tablesdb\/list-rows.md",
@@ -9672,7 +9690,7 @@
"x-appwrite": {
"method": "createRow",
"group": "rows",
"weight": 429,
"weight": 430,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-row.md",
@@ -9828,7 +9846,7 @@
"x-appwrite": {
"method": "getRow",
"group": "rows",
"weight": 430,
"weight": 431,
"cookies": false,
"type": "",
"demo": "tablesdb\/get-row.md",
@@ -9938,7 +9956,7 @@
"x-appwrite": {
"method": "upsertRow",
"group": "rows",
"weight": 433,
"weight": 434,
"cookies": false,
"type": "",
"demo": "tablesdb\/upsert-row.md",
@@ -10088,7 +10106,7 @@
"x-appwrite": {
"method": "updateRow",
"group": "rows",
"weight": 431,
"weight": 432,
"cookies": false,
"type": "",
"demo": "tablesdb\/update-row.md",
@@ -10199,7 +10217,7 @@
"x-appwrite": {
"method": "deleteRow",
"group": "rows",
"weight": 435,
"weight": 436,
"cookies": false,
"type": "",
"demo": "tablesdb\/delete-row.md",
@@ -10305,7 +10323,7 @@
"x-appwrite": {
"method": "decrementRowColumn",
"group": "rows",
"weight": 440,
"weight": 441,
"cookies": false,
"type": "",
"demo": "tablesdb\/decrement-row-column.md",
@@ -10431,7 +10449,7 @@
"x-appwrite": {
"method": "incrementRowColumn",
"group": "rows",
"weight": 439,
"weight": 440,
"cookies": false,
"type": "",
"demo": "tablesdb\/increment-row-column.md",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+69 -51
View File
@@ -407,8 +407,8 @@
"cookies": false,
"type": "",
"demo": "account\/create-jwt.md",
"rate-limit": 100,
"rate-time": 3600,
"rate-limit": 120,
"rate-time": 60,
"rate-key": "url:{url},userId:{userId}",
"scope": "account",
"platforms": [
@@ -429,7 +429,23 @@
"Session": [],
"JWT": []
}
]
],
"requestBody": {
"content": {
"application\/json": {
"schema": {
"type": "object",
"properties": {
"duration": {
"type": "integer",
"description": "Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.",
"x-example": 0
}
}
}
}
}
}
}
},
"\/account\/logs": {
@@ -535,7 +551,7 @@
"x-appwrite": {
"method": "updateMFA",
"group": "mfa",
"weight": 306,
"weight": 307,
"cookies": false,
"type": "",
"demo": "account\/update-mfa.md",
@@ -607,7 +623,7 @@
"x-appwrite": {
"method": "createMfaAuthenticator",
"group": "mfa",
"weight": 308,
"weight": 309,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-authenticator.md",
@@ -731,7 +747,7 @@
"x-appwrite": {
"method": "updateMfaAuthenticator",
"group": "mfa",
"weight": 309,
"weight": 310,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-authenticator.md",
@@ -871,7 +887,7 @@
"x-appwrite": {
"method": "deleteMfaAuthenticator",
"group": "mfa",
"weight": 310,
"weight": 311,
"cookies": false,
"type": "",
"demo": "account\/delete-mfa-authenticator.md",
@@ -995,7 +1011,7 @@
"x-appwrite": {
"method": "createMfaChallenge",
"group": "mfa",
"weight": 314,
"weight": 315,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-challenge.md",
@@ -1129,7 +1145,7 @@
"x-appwrite": {
"method": "updateMfaChallenge",
"group": "mfa",
"weight": 315,
"weight": 316,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-challenge.md",
@@ -1267,7 +1283,7 @@
"x-appwrite": {
"method": "listMfaFactors",
"group": "mfa",
"weight": 307,
"weight": 308,
"cookies": false,
"type": "",
"demo": "account\/list-mfa-factors.md",
@@ -1368,7 +1384,7 @@
"x-appwrite": {
"method": "getMfaRecoveryCodes",
"group": "mfa",
"weight": 313,
"weight": 314,
"cookies": false,
"type": "",
"demo": "account\/get-mfa-recovery-codes.md",
@@ -1467,7 +1483,7 @@
"x-appwrite": {
"method": "createMfaRecoveryCodes",
"group": "mfa",
"weight": 311,
"weight": 312,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-recovery-codes.md",
@@ -1566,7 +1582,7 @@
"x-appwrite": {
"method": "updateMfaRecoveryCodes",
"group": "mfa",
"weight": 312,
"weight": 313,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-recovery-codes.md",
@@ -2558,7 +2574,8 @@
"yandex",
"zoho",
"zoom",
"mock"
"mock",
"mock-unverified"
],
"x-enum-name": "OAuthProvider",
"x-enum-keys": []
@@ -3488,7 +3505,8 @@
"yandex",
"zoho",
"zoom",
"mock"
"mock",
"mock-unverified"
],
"x-enum-name": "OAuthProvider",
"x-enum-keys": []
@@ -5825,7 +5843,7 @@
"avif",
"gif"
],
"x-enum-name": null,
"x-enum-name": "ImageFormat",
"x-enum-keys": [],
"default": ""
},
@@ -5858,7 +5876,7 @@
"x-appwrite": {
"method": "listTransactions",
"group": "transactions",
"weight": 380,
"weight": 381,
"cookies": false,
"type": "",
"demo": "databases\/list-transactions.md",
@@ -5925,7 +5943,7 @@
"x-appwrite": {
"method": "createTransaction",
"group": "transactions",
"weight": 376,
"weight": 377,
"cookies": false,
"type": "",
"demo": "databases\/create-transaction.md",
@@ -5995,7 +6013,7 @@
"x-appwrite": {
"method": "getTransaction",
"group": "transactions",
"weight": 377,
"weight": 378,
"cookies": false,
"type": "",
"demo": "databases\/get-transaction.md",
@@ -6059,7 +6077,7 @@
"x-appwrite": {
"method": "updateTransaction",
"group": "transactions",
"weight": 378,
"weight": 379,
"cookies": false,
"type": "",
"demo": "databases\/update-transaction.md",
@@ -6137,7 +6155,7 @@
"x-appwrite": {
"method": "deleteTransaction",
"group": "transactions",
"weight": 379,
"weight": 380,
"cookies": false,
"type": "",
"demo": "databases\/delete-transaction.md",
@@ -6203,7 +6221,7 @@
"x-appwrite": {
"method": "createOperations",
"group": "transactions",
"weight": 381,
"weight": 382,
"cookies": false,
"type": "",
"demo": "databases\/create-operations.md",
@@ -6288,7 +6306,7 @@
"x-appwrite": {
"method": "listDocuments",
"group": "documents",
"weight": 339,
"weight": 340,
"cookies": false,
"type": "",
"demo": "databases\/list-documents.md",
@@ -6400,7 +6418,7 @@
"x-appwrite": {
"method": "createDocument",
"group": "documents",
"weight": 331,
"weight": 332,
"cookies": false,
"type": "",
"demo": "databases\/create-document.md",
@@ -6561,7 +6579,7 @@
"x-appwrite": {
"method": "getDocument",
"group": "documents",
"weight": 332,
"weight": 333,
"cookies": false,
"type": "",
"demo": "databases\/get-document.md",
@@ -6672,7 +6690,7 @@
"x-appwrite": {
"method": "upsertDocument",
"group": "documents",
"weight": 335,
"weight": 336,
"cookies": false,
"type": "",
"demo": "databases\/upsert-document.md",
@@ -6827,7 +6845,7 @@
"x-appwrite": {
"method": "updateDocument",
"group": "documents",
"weight": 333,
"weight": 334,
"cookies": false,
"type": "",
"demo": "databases\/update-document.md",
@@ -6939,7 +6957,7 @@
"x-appwrite": {
"method": "deleteDocument",
"group": "documents",
"weight": 337,
"weight": 338,
"cookies": false,
"type": "",
"demo": "databases\/delete-document.md",
@@ -7046,7 +7064,7 @@
"x-appwrite": {
"method": "decrementDocumentAttribute",
"group": "documents",
"weight": 342,
"weight": 343,
"cookies": false,
"type": "",
"demo": "databases\/decrement-document-attribute.md",
@@ -7173,7 +7191,7 @@
"x-appwrite": {
"method": "incrementDocumentAttribute",
"group": "documents",
"weight": 341,
"weight": 342,
"cookies": false,
"type": "",
"demo": "databases\/increment-document-attribute.md",
@@ -7300,7 +7318,7 @@
"x-appwrite": {
"method": "listExecutions",
"group": "executions",
"weight": 472,
"weight": 473,
"cookies": false,
"type": "",
"demo": "functions\/list-executions.md",
@@ -7387,7 +7405,7 @@
"x-appwrite": {
"method": "createExecution",
"group": "executions",
"weight": 470,
"weight": 471,
"cookies": false,
"type": "",
"demo": "functions\/create-execution.md",
@@ -7505,7 +7523,7 @@
"x-appwrite": {
"method": "getExecution",
"group": "executions",
"weight": 471,
"weight": 472,
"cookies": false,
"type": "",
"demo": "functions\/get-execution.md",
@@ -7580,7 +7598,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 241,
"weight": 242,
"cookies": false,
"type": "graphql",
"demo": "graphql\/query.md",
@@ -7634,7 +7652,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 240,
"weight": 241,
"cookies": false,
"type": "graphql",
"demo": "graphql\/mutation.md",
@@ -8120,7 +8138,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 290,
"weight": 291,
"cookies": false,
"type": "",
"demo": "messaging\/create-subscriber.md",
@@ -8204,7 +8222,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 294,
"weight": 295,
"cookies": false,
"type": "",
"demo": "messaging\/delete-subscriber.md",
@@ -9113,7 +9131,7 @@
"x-appwrite": {
"method": "listTransactions",
"group": "transactions",
"weight": 445,
"weight": 446,
"cookies": false,
"type": "",
"demo": "tablesdb\/list-transactions.md",
@@ -9183,7 +9201,7 @@
"x-appwrite": {
"method": "createTransaction",
"group": "transactions",
"weight": 441,
"weight": 442,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-transaction.md",
@@ -9256,7 +9274,7 @@
"x-appwrite": {
"method": "getTransaction",
"group": "transactions",
"weight": 442,
"weight": 443,
"cookies": false,
"type": "",
"demo": "tablesdb\/get-transaction.md",
@@ -9323,7 +9341,7 @@
"x-appwrite": {
"method": "updateTransaction",
"group": "transactions",
"weight": 443,
"weight": 444,
"cookies": false,
"type": "",
"demo": "tablesdb\/update-transaction.md",
@@ -9404,7 +9422,7 @@
"x-appwrite": {
"method": "deleteTransaction",
"group": "transactions",
"weight": 444,
"weight": 445,
"cookies": false,
"type": "",
"demo": "tablesdb\/delete-transaction.md",
@@ -9473,7 +9491,7 @@
"x-appwrite": {
"method": "createOperations",
"group": "transactions",
"weight": 446,
"weight": 447,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-operations.md",
@@ -9561,7 +9579,7 @@
"x-appwrite": {
"method": "listRows",
"group": "rows",
"weight": 437,
"weight": 438,
"cookies": false,
"type": "",
"demo": "tablesdb\/list-rows.md",
@@ -9672,7 +9690,7 @@
"x-appwrite": {
"method": "createRow",
"group": "rows",
"weight": 429,
"weight": 430,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-row.md",
@@ -9828,7 +9846,7 @@
"x-appwrite": {
"method": "getRow",
"group": "rows",
"weight": 430,
"weight": 431,
"cookies": false,
"type": "",
"demo": "tablesdb\/get-row.md",
@@ -9938,7 +9956,7 @@
"x-appwrite": {
"method": "upsertRow",
"group": "rows",
"weight": 433,
"weight": 434,
"cookies": false,
"type": "",
"demo": "tablesdb\/upsert-row.md",
@@ -10088,7 +10106,7 @@
"x-appwrite": {
"method": "updateRow",
"group": "rows",
"weight": 431,
"weight": 432,
"cookies": false,
"type": "",
"demo": "tablesdb\/update-row.md",
@@ -10199,7 +10217,7 @@
"x-appwrite": {
"method": "deleteRow",
"group": "rows",
"weight": 435,
"weight": 436,
"cookies": false,
"type": "",
"demo": "tablesdb\/delete-row.md",
@@ -10305,7 +10323,7 @@
"x-appwrite": {
"method": "decrementRowColumn",
"group": "rows",
"weight": 440,
"weight": 441,
"cookies": false,
"type": "",
"demo": "tablesdb\/decrement-row-column.md",
@@ -10431,7 +10449,7 @@
"x-appwrite": {
"method": "incrementRowColumn",
"group": "rows",
"weight": 439,
"weight": 440,
"cookies": false,
"type": "",
"demo": "tablesdb\/increment-row-column.md",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+69 -50
View File
@@ -464,8 +464,8 @@
"cookies": false,
"type": "",
"demo": "account\/create-jwt.md",
"rate-limit": 100,
"rate-time": 3600,
"rate-limit": 120,
"rate-time": 60,
"rate-key": "url:{url},userId:{userId}",
"scope": "account",
"platforms": [
@@ -486,6 +486,23 @@
"Session": [],
"JWT": []
}
],
"parameters": [
{
"name": "payload",
"in": "body",
"schema": {
"type": "object",
"properties": {
"duration": {
"type": "integer",
"description": "Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.",
"default": 900,
"x-example": 0
}
}
}
}
]
}
},
@@ -591,7 +608,7 @@
"x-appwrite": {
"method": "updateMFA",
"group": "mfa",
"weight": 306,
"weight": 307,
"cookies": false,
"type": "",
"demo": "account\/update-mfa.md",
@@ -666,7 +683,7 @@
"x-appwrite": {
"method": "createMfaAuthenticator",
"group": "mfa",
"weight": 308,
"weight": 309,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-authenticator.md",
@@ -790,7 +807,7 @@
"x-appwrite": {
"method": "updateMfaAuthenticator",
"group": "mfa",
"weight": 309,
"weight": 310,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-authenticator.md",
@@ -931,7 +948,7 @@
"x-appwrite": {
"method": "deleteMfaAuthenticator",
"group": "mfa",
"weight": 310,
"weight": 311,
"cookies": false,
"type": "",
"demo": "account\/delete-mfa-authenticator.md",
@@ -1055,7 +1072,7 @@
"x-appwrite": {
"method": "createMfaChallenge",
"group": "mfa",
"weight": 314,
"weight": 315,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-challenge.md",
@@ -1192,7 +1209,7 @@
"x-appwrite": {
"method": "updateMfaChallenge",
"group": "mfa",
"weight": 315,
"weight": 316,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-challenge.md",
@@ -1332,7 +1349,7 @@
"x-appwrite": {
"method": "listMfaFactors",
"group": "mfa",
"weight": 307,
"weight": 308,
"cookies": false,
"type": "",
"demo": "account\/list-mfa-factors.md",
@@ -1433,7 +1450,7 @@
"x-appwrite": {
"method": "getMfaRecoveryCodes",
"group": "mfa",
"weight": 313,
"weight": 314,
"cookies": false,
"type": "",
"demo": "account\/get-mfa-recovery-codes.md",
@@ -1534,7 +1551,7 @@
"x-appwrite": {
"method": "createMfaRecoveryCodes",
"group": "mfa",
"weight": 311,
"weight": 312,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-recovery-codes.md",
@@ -1635,7 +1652,7 @@
"x-appwrite": {
"method": "updateMfaRecoveryCodes",
"group": "mfa",
"weight": 312,
"weight": 313,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-recovery-codes.md",
@@ -2667,7 +2684,8 @@
"yandex",
"zoho",
"zoom",
"mock"
"mock",
"mock-unverified"
],
"x-enum-name": "OAuthProvider",
"x-enum-keys": [],
@@ -3621,7 +3639,8 @@
"yandex",
"zoho",
"zoom",
"mock"
"mock",
"mock-unverified"
],
"x-enum-name": "OAuthProvider",
"x-enum-keys": [],
@@ -5931,7 +5950,7 @@
"avif",
"gif"
],
"x-enum-name": null,
"x-enum-name": "ImageFormat",
"x-enum-keys": [],
"default": "",
"in": "query"
@@ -5963,7 +5982,7 @@
"x-appwrite": {
"method": "listTransactions",
"group": "transactions",
"weight": 380,
"weight": 381,
"cookies": false,
"type": "",
"demo": "databases\/list-transactions.md",
@@ -6030,7 +6049,7 @@
"x-appwrite": {
"method": "createTransaction",
"group": "transactions",
"weight": 376,
"weight": 377,
"cookies": false,
"type": "",
"demo": "databases\/create-transaction.md",
@@ -6100,7 +6119,7 @@
"x-appwrite": {
"method": "getTransaction",
"group": "transactions",
"weight": 377,
"weight": 378,
"cookies": false,
"type": "",
"demo": "databases\/get-transaction.md",
@@ -6163,7 +6182,7 @@
"x-appwrite": {
"method": "updateTransaction",
"group": "transactions",
"weight": 378,
"weight": 379,
"cookies": false,
"type": "",
"demo": "databases\/update-transaction.md",
@@ -6242,7 +6261,7 @@
"x-appwrite": {
"method": "deleteTransaction",
"group": "transactions",
"weight": 379,
"weight": 380,
"cookies": false,
"type": "",
"demo": "databases\/delete-transaction.md",
@@ -6307,7 +6326,7 @@
"x-appwrite": {
"method": "createOperations",
"group": "transactions",
"weight": 381,
"weight": 382,
"cookies": false,
"type": "",
"demo": "databases\/create-operations.md",
@@ -6388,7 +6407,7 @@
"x-appwrite": {
"method": "listDocuments",
"group": "documents",
"weight": 339,
"weight": 340,
"cookies": false,
"type": "",
"demo": "databases\/list-documents.md",
@@ -6492,7 +6511,7 @@
"x-appwrite": {
"method": "createDocument",
"group": "documents",
"weight": 331,
"weight": 332,
"cookies": false,
"type": "",
"demo": "databases\/create-document.md",
@@ -6651,7 +6670,7 @@
"x-appwrite": {
"method": "getDocument",
"group": "documents",
"weight": 332,
"weight": 333,
"cookies": false,
"type": "",
"demo": "databases\/get-document.md",
@@ -6754,7 +6773,7 @@
"x-appwrite": {
"method": "upsertDocument",
"group": "documents",
"weight": 335,
"weight": 336,
"cookies": false,
"type": "",
"demo": "databases\/upsert-document.md",
@@ -6905,7 +6924,7 @@
"x-appwrite": {
"method": "updateDocument",
"group": "documents",
"weight": 333,
"weight": 334,
"cookies": false,
"type": "",
"demo": "databases\/update-document.md",
@@ -7015,7 +7034,7 @@
"x-appwrite": {
"method": "deleteDocument",
"group": "documents",
"weight": 337,
"weight": 338,
"cookies": false,
"type": "",
"demo": "databases\/delete-document.md",
@@ -7116,7 +7135,7 @@
"x-appwrite": {
"method": "decrementDocumentAttribute",
"group": "documents",
"weight": 342,
"weight": 343,
"cookies": false,
"type": "",
"demo": "databases\/decrement-document-attribute.md",
@@ -7237,7 +7256,7 @@
"x-appwrite": {
"method": "incrementDocumentAttribute",
"group": "documents",
"weight": 341,
"weight": 342,
"cookies": false,
"type": "",
"demo": "databases\/increment-document-attribute.md",
@@ -7356,7 +7375,7 @@
"x-appwrite": {
"method": "listExecutions",
"group": "executions",
"weight": 472,
"weight": 473,
"cookies": false,
"type": "",
"demo": "functions\/list-executions.md",
@@ -7439,7 +7458,7 @@
"x-appwrite": {
"method": "createExecution",
"group": "executions",
"weight": 470,
"weight": 471,
"cookies": false,
"type": "",
"demo": "functions\/create-execution.md",
@@ -7558,7 +7577,7 @@
"x-appwrite": {
"method": "getExecution",
"group": "executions",
"weight": 471,
"weight": 472,
"cookies": false,
"type": "",
"demo": "functions\/get-execution.md",
@@ -7630,7 +7649,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 241,
"weight": 242,
"cookies": false,
"type": "graphql",
"demo": "graphql\/query.md",
@@ -7705,7 +7724,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 240,
"weight": 241,
"cookies": false,
"type": "graphql",
"demo": "graphql\/mutation.md",
@@ -8204,7 +8223,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 290,
"weight": 291,
"cookies": false,
"type": "",
"demo": "messaging\/create-subscriber.md",
@@ -8289,7 +8308,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 294,
"weight": 295,
"cookies": false,
"type": "",
"demo": "messaging\/delete-subscriber.md",
@@ -9146,7 +9165,7 @@
"x-appwrite": {
"method": "listTransactions",
"group": "transactions",
"weight": 445,
"weight": 446,
"cookies": false,
"type": "",
"demo": "tablesdb\/list-transactions.md",
@@ -9216,7 +9235,7 @@
"x-appwrite": {
"method": "createTransaction",
"group": "transactions",
"weight": 441,
"weight": 442,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-transaction.md",
@@ -9289,7 +9308,7 @@
"x-appwrite": {
"method": "getTransaction",
"group": "transactions",
"weight": 442,
"weight": 443,
"cookies": false,
"type": "",
"demo": "tablesdb\/get-transaction.md",
@@ -9355,7 +9374,7 @@
"x-appwrite": {
"method": "updateTransaction",
"group": "transactions",
"weight": 443,
"weight": 444,
"cookies": false,
"type": "",
"demo": "tablesdb\/update-transaction.md",
@@ -9437,7 +9456,7 @@
"x-appwrite": {
"method": "deleteTransaction",
"group": "transactions",
"weight": 444,
"weight": 445,
"cookies": false,
"type": "",
"demo": "tablesdb\/delete-transaction.md",
@@ -9505,7 +9524,7 @@
"x-appwrite": {
"method": "createOperations",
"group": "transactions",
"weight": 446,
"weight": 447,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-operations.md",
@@ -9589,7 +9608,7 @@
"x-appwrite": {
"method": "listRows",
"group": "rows",
"weight": 437,
"weight": 438,
"cookies": false,
"type": "",
"demo": "tablesdb\/list-rows.md",
@@ -9692,7 +9711,7 @@
"x-appwrite": {
"method": "createRow",
"group": "rows",
"weight": 429,
"weight": 430,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-row.md",
@@ -9846,7 +9865,7 @@
"x-appwrite": {
"method": "getRow",
"group": "rows",
"weight": 430,
"weight": 431,
"cookies": false,
"type": "",
"demo": "tablesdb\/get-row.md",
@@ -9948,7 +9967,7 @@
"x-appwrite": {
"method": "upsertRow",
"group": "rows",
"weight": 433,
"weight": 434,
"cookies": false,
"type": "",
"demo": "tablesdb\/upsert-row.md",
@@ -10094,7 +10113,7 @@
"x-appwrite": {
"method": "updateRow",
"group": "rows",
"weight": 431,
"weight": 432,
"cookies": false,
"type": "",
"demo": "tablesdb\/update-row.md",
@@ -10203,7 +10222,7 @@
"x-appwrite": {
"method": "deleteRow",
"group": "rows",
"weight": 435,
"weight": 436,
"cookies": false,
"type": "",
"demo": "tablesdb\/delete-row.md",
@@ -10303,7 +10322,7 @@
"x-appwrite": {
"method": "decrementRowColumn",
"group": "rows",
"weight": 440,
"weight": 441,
"cookies": false,
"type": "",
"demo": "tablesdb\/decrement-row-column.md",
@@ -10423,7 +10442,7 @@
"x-appwrite": {
"method": "incrementRowColumn",
"group": "rows",
"weight": 439,
"weight": 440,
"cookies": false,
"type": "",
"demo": "tablesdb\/increment-row-column.md",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+69 -50
View File
@@ -464,8 +464,8 @@
"cookies": false,
"type": "",
"demo": "account\/create-jwt.md",
"rate-limit": 100,
"rate-time": 3600,
"rate-limit": 120,
"rate-time": 60,
"rate-key": "url:{url},userId:{userId}",
"scope": "account",
"platforms": [
@@ -486,6 +486,23 @@
"Session": [],
"JWT": []
}
],
"parameters": [
{
"name": "payload",
"in": "body",
"schema": {
"type": "object",
"properties": {
"duration": {
"type": "integer",
"description": "Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.",
"default": 900,
"x-example": 0
}
}
}
}
]
}
},
@@ -591,7 +608,7 @@
"x-appwrite": {
"method": "updateMFA",
"group": "mfa",
"weight": 306,
"weight": 307,
"cookies": false,
"type": "",
"demo": "account\/update-mfa.md",
@@ -666,7 +683,7 @@
"x-appwrite": {
"method": "createMfaAuthenticator",
"group": "mfa",
"weight": 308,
"weight": 309,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-authenticator.md",
@@ -790,7 +807,7 @@
"x-appwrite": {
"method": "updateMfaAuthenticator",
"group": "mfa",
"weight": 309,
"weight": 310,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-authenticator.md",
@@ -931,7 +948,7 @@
"x-appwrite": {
"method": "deleteMfaAuthenticator",
"group": "mfa",
"weight": 310,
"weight": 311,
"cookies": false,
"type": "",
"demo": "account\/delete-mfa-authenticator.md",
@@ -1055,7 +1072,7 @@
"x-appwrite": {
"method": "createMfaChallenge",
"group": "mfa",
"weight": 314,
"weight": 315,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-challenge.md",
@@ -1192,7 +1209,7 @@
"x-appwrite": {
"method": "updateMfaChallenge",
"group": "mfa",
"weight": 315,
"weight": 316,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-challenge.md",
@@ -1332,7 +1349,7 @@
"x-appwrite": {
"method": "listMfaFactors",
"group": "mfa",
"weight": 307,
"weight": 308,
"cookies": false,
"type": "",
"demo": "account\/list-mfa-factors.md",
@@ -1433,7 +1450,7 @@
"x-appwrite": {
"method": "getMfaRecoveryCodes",
"group": "mfa",
"weight": 313,
"weight": 314,
"cookies": false,
"type": "",
"demo": "account\/get-mfa-recovery-codes.md",
@@ -1534,7 +1551,7 @@
"x-appwrite": {
"method": "createMfaRecoveryCodes",
"group": "mfa",
"weight": 311,
"weight": 312,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-recovery-codes.md",
@@ -1635,7 +1652,7 @@
"x-appwrite": {
"method": "updateMfaRecoveryCodes",
"group": "mfa",
"weight": 312,
"weight": 313,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-recovery-codes.md",
@@ -2667,7 +2684,8 @@
"yandex",
"zoho",
"zoom",
"mock"
"mock",
"mock-unverified"
],
"x-enum-name": "OAuthProvider",
"x-enum-keys": [],
@@ -3621,7 +3639,8 @@
"yandex",
"zoho",
"zoom",
"mock"
"mock",
"mock-unverified"
],
"x-enum-name": "OAuthProvider",
"x-enum-keys": [],
@@ -5931,7 +5950,7 @@
"avif",
"gif"
],
"x-enum-name": null,
"x-enum-name": "ImageFormat",
"x-enum-keys": [],
"default": "",
"in": "query"
@@ -5963,7 +5982,7 @@
"x-appwrite": {
"method": "listTransactions",
"group": "transactions",
"weight": 380,
"weight": 381,
"cookies": false,
"type": "",
"demo": "databases\/list-transactions.md",
@@ -6030,7 +6049,7 @@
"x-appwrite": {
"method": "createTransaction",
"group": "transactions",
"weight": 376,
"weight": 377,
"cookies": false,
"type": "",
"demo": "databases\/create-transaction.md",
@@ -6100,7 +6119,7 @@
"x-appwrite": {
"method": "getTransaction",
"group": "transactions",
"weight": 377,
"weight": 378,
"cookies": false,
"type": "",
"demo": "databases\/get-transaction.md",
@@ -6163,7 +6182,7 @@
"x-appwrite": {
"method": "updateTransaction",
"group": "transactions",
"weight": 378,
"weight": 379,
"cookies": false,
"type": "",
"demo": "databases\/update-transaction.md",
@@ -6242,7 +6261,7 @@
"x-appwrite": {
"method": "deleteTransaction",
"group": "transactions",
"weight": 379,
"weight": 380,
"cookies": false,
"type": "",
"demo": "databases\/delete-transaction.md",
@@ -6307,7 +6326,7 @@
"x-appwrite": {
"method": "createOperations",
"group": "transactions",
"weight": 381,
"weight": 382,
"cookies": false,
"type": "",
"demo": "databases\/create-operations.md",
@@ -6388,7 +6407,7 @@
"x-appwrite": {
"method": "listDocuments",
"group": "documents",
"weight": 339,
"weight": 340,
"cookies": false,
"type": "",
"demo": "databases\/list-documents.md",
@@ -6492,7 +6511,7 @@
"x-appwrite": {
"method": "createDocument",
"group": "documents",
"weight": 331,
"weight": 332,
"cookies": false,
"type": "",
"demo": "databases\/create-document.md",
@@ -6651,7 +6670,7 @@
"x-appwrite": {
"method": "getDocument",
"group": "documents",
"weight": 332,
"weight": 333,
"cookies": false,
"type": "",
"demo": "databases\/get-document.md",
@@ -6754,7 +6773,7 @@
"x-appwrite": {
"method": "upsertDocument",
"group": "documents",
"weight": 335,
"weight": 336,
"cookies": false,
"type": "",
"demo": "databases\/upsert-document.md",
@@ -6905,7 +6924,7 @@
"x-appwrite": {
"method": "updateDocument",
"group": "documents",
"weight": 333,
"weight": 334,
"cookies": false,
"type": "",
"demo": "databases\/update-document.md",
@@ -7015,7 +7034,7 @@
"x-appwrite": {
"method": "deleteDocument",
"group": "documents",
"weight": 337,
"weight": 338,
"cookies": false,
"type": "",
"demo": "databases\/delete-document.md",
@@ -7116,7 +7135,7 @@
"x-appwrite": {
"method": "decrementDocumentAttribute",
"group": "documents",
"weight": 342,
"weight": 343,
"cookies": false,
"type": "",
"demo": "databases\/decrement-document-attribute.md",
@@ -7237,7 +7256,7 @@
"x-appwrite": {
"method": "incrementDocumentAttribute",
"group": "documents",
"weight": 341,
"weight": 342,
"cookies": false,
"type": "",
"demo": "databases\/increment-document-attribute.md",
@@ -7356,7 +7375,7 @@
"x-appwrite": {
"method": "listExecutions",
"group": "executions",
"weight": 472,
"weight": 473,
"cookies": false,
"type": "",
"demo": "functions\/list-executions.md",
@@ -7439,7 +7458,7 @@
"x-appwrite": {
"method": "createExecution",
"group": "executions",
"weight": 470,
"weight": 471,
"cookies": false,
"type": "",
"demo": "functions\/create-execution.md",
@@ -7558,7 +7577,7 @@
"x-appwrite": {
"method": "getExecution",
"group": "executions",
"weight": 471,
"weight": 472,
"cookies": false,
"type": "",
"demo": "functions\/get-execution.md",
@@ -7630,7 +7649,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 241,
"weight": 242,
"cookies": false,
"type": "graphql",
"demo": "graphql\/query.md",
@@ -7705,7 +7724,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 240,
"weight": 241,
"cookies": false,
"type": "graphql",
"demo": "graphql\/mutation.md",
@@ -8204,7 +8223,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 290,
"weight": 291,
"cookies": false,
"type": "",
"demo": "messaging\/create-subscriber.md",
@@ -8289,7 +8308,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 294,
"weight": 295,
"cookies": false,
"type": "",
"demo": "messaging\/delete-subscriber.md",
@@ -9146,7 +9165,7 @@
"x-appwrite": {
"method": "listTransactions",
"group": "transactions",
"weight": 445,
"weight": 446,
"cookies": false,
"type": "",
"demo": "tablesdb\/list-transactions.md",
@@ -9216,7 +9235,7 @@
"x-appwrite": {
"method": "createTransaction",
"group": "transactions",
"weight": 441,
"weight": 442,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-transaction.md",
@@ -9289,7 +9308,7 @@
"x-appwrite": {
"method": "getTransaction",
"group": "transactions",
"weight": 442,
"weight": 443,
"cookies": false,
"type": "",
"demo": "tablesdb\/get-transaction.md",
@@ -9355,7 +9374,7 @@
"x-appwrite": {
"method": "updateTransaction",
"group": "transactions",
"weight": 443,
"weight": 444,
"cookies": false,
"type": "",
"demo": "tablesdb\/update-transaction.md",
@@ -9437,7 +9456,7 @@
"x-appwrite": {
"method": "deleteTransaction",
"group": "transactions",
"weight": 444,
"weight": 445,
"cookies": false,
"type": "",
"demo": "tablesdb\/delete-transaction.md",
@@ -9505,7 +9524,7 @@
"x-appwrite": {
"method": "createOperations",
"group": "transactions",
"weight": 446,
"weight": 447,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-operations.md",
@@ -9589,7 +9608,7 @@
"x-appwrite": {
"method": "listRows",
"group": "rows",
"weight": 437,
"weight": 438,
"cookies": false,
"type": "",
"demo": "tablesdb\/list-rows.md",
@@ -9692,7 +9711,7 @@
"x-appwrite": {
"method": "createRow",
"group": "rows",
"weight": 429,
"weight": 430,
"cookies": false,
"type": "",
"demo": "tablesdb\/create-row.md",
@@ -9846,7 +9865,7 @@
"x-appwrite": {
"method": "getRow",
"group": "rows",
"weight": 430,
"weight": 431,
"cookies": false,
"type": "",
"demo": "tablesdb\/get-row.md",
@@ -9948,7 +9967,7 @@
"x-appwrite": {
"method": "upsertRow",
"group": "rows",
"weight": 433,
"weight": 434,
"cookies": false,
"type": "",
"demo": "tablesdb\/upsert-row.md",
@@ -10094,7 +10113,7 @@
"x-appwrite": {
"method": "updateRow",
"group": "rows",
"weight": 431,
"weight": 432,
"cookies": false,
"type": "",
"demo": "tablesdb\/update-row.md",
@@ -10203,7 +10222,7 @@
"x-appwrite": {
"method": "deleteRow",
"group": "rows",
"weight": 435,
"weight": 436,
"cookies": false,
"type": "",
"demo": "tablesdb\/delete-row.md",
@@ -10303,7 +10322,7 @@
"x-appwrite": {
"method": "decrementRowColumn",
"group": "rows",
"weight": 440,
"weight": 441,
"cookies": false,
"type": "",
"demo": "tablesdb\/decrement-row-column.md",
@@ -10423,7 +10442,7 @@
"x-appwrite": {
"method": "incrementRowColumn",
"group": "rows",
"weight": 439,
"weight": 440,
"cookies": false,
"type": "",
"demo": "tablesdb\/increment-row-column.md",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -3
View File
@@ -3,6 +3,4 @@
use Utopia\Image\Image;
use Utopia\System\System;
if (\class_exists('Imagick')) {
Image::setResourceLimit('memory', intval(System::getEnv('_APP_IMAGES_RESOURCE_LIMIT_MEMORY', 1024*1024*64)));
}
Image::setResourceLimit('memory', intval(System::getEnv('_APP_IMAGES_RESOURCE_LIMIT_MEMORY', 1024*1024*64)));
+68 -87
View File
@@ -68,6 +68,7 @@ use Utopia\Validator;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
use Utopia\Validator\Boolean;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
@@ -206,10 +207,10 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, arr
}
$createSession = function (string $userId, string $secret, Request $request, Response $response, User $user, Database $dbForProject, Document $project, array $platform, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Store $store, ProofsToken $proofForToken, ProofsCode $proofForCode, Authorization $authorization) {
$createSession = function (string $userId, string $secret, Request $request, Response $response, User $user, Database $dbForProject, Document $project, array $platform, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Store $store, ProofsToken $proofForToken, ProofsCode $proofForCode) {
/** @var Appwrite\Utopia\Database\Documents\User $userFromRequest */
$userFromRequest = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId));
$userFromRequest = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId));
if ($userFromRequest->isEmpty()) {
throw new Exception(Exception::USER_INVALID_TOKEN);
@@ -265,7 +266,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
$detector->getDevice()
));
$authorization->addRole(Role::user($user->getId())->toString());
Authorization::setRole(Role::user($user->getId())->toString());
$session = $dbForProject->createDocument('sessions', $session
->setAttribute('$permissions', [
@@ -274,7 +275,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
Permission::delete(Role::user($user->getId())),
]));
$authorization->skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId()));
Authorization::skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId()));
$dbForProject->purgeCachedDocument('users', $user->getId());
// Magic URL + Email OTP
@@ -375,9 +376,8 @@ App::post('/v1/account')
->inject('user')
->inject('project')
->inject('dbForProject')
->inject('authorization')
->inject('hooks')
->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Authorization $authorization, Hooks $hooks) {
->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Hooks $hooks) {
$email = \strtolower($email);
if ('console' === $project->getId()) {
@@ -469,9 +469,9 @@ App::post('/v1/account')
]);
$user->removeAttribute('$sequence');
$user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user));
$user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
try {
$target = $authorization->skip(fn () => $dbForProject->createDocument('targets', new Document([
$target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
@@ -497,9 +497,9 @@ App::post('/v1/account')
throw new Exception(Exception::USER_ALREADY_EXISTS);
}
$authorization->removeRole(Role::guests()->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::users()->toString());
Authorization::unsetRole(Role::guests()->toString());
Authorization::setRole(Role::user($user->getId())->toString());
Authorization::setRole(Role::users()->toString());
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -975,8 +975,7 @@ App::post('/v1/account/sessions/email')
->inject('store')
->inject('proofForPassword')
->inject('proofForToken')
->inject('authorization')
->action(function (string $email, string $password, Request $request, Response $response, User $user, Database $dbForProject, Document $project, array $platform, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken, Authorization $authorization) {
->action(function (string $email, string $password, Request $request, Response $response, User $user, Database $dbForProject, Document $project, array $platform, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken) {
$email = \strtolower($email);
$protocol = $request->getProtocol();
@@ -1021,7 +1020,7 @@ App::post('/v1/account/sessions/email')
$detector->getDevice()
));
$authorization->addRole(Role::user($user->getId())->toString());
Authorization::setRole(Role::user($user->getId())->toString());
// Re-hash if not using recommended algo
if ($user->getAttribute('hash') !== $proofForPassword->getHash()->getName()) {
@@ -1120,8 +1119,7 @@ App::post('/v1/account/sessions/anonymous')
->inject('store')
->inject('proofForPassword')
->inject('proofForToken')
->inject('authorization')
->action(function (Request $request, Response $response, Locale $locale, User $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken, Authorization $authorization) {
->action(function (Request $request, Response $response, Locale $locale, User $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken) {
$protocol = $request->getProtocol();
if ('console' === $project->getId()) {
@@ -1166,7 +1164,7 @@ App::post('/v1/account/sessions/anonymous')
'accessedAt' => DateTime::now(),
]);
$user->removeAttribute('$sequence');
$user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user));
Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
// Create session token
$duration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG;
@@ -1192,7 +1190,7 @@ App::post('/v1/account/sessions/anonymous')
$detector->getDevice()
));
$authorization->addRole(Role::user($user->getId())->toString());
Authorization::setRole(Role::user($user->getId())->toString());
$session = $dbForProject->createDocument('sessions', $session->setAttribute('$permissions', [
Permission::read(Role::user($user->getId())),
@@ -1274,7 +1272,6 @@ App::post('/v1/account/sessions/token')
->inject('store')
->inject('proofForToken')
->inject('proofForCode')
->inject('authorization')
->action($createSession);
App::get('/v1/account/sessions/oauth2/:provider')
@@ -1471,8 +1468,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
->inject('store')
->inject('proofForPassword')
->inject('proofForToken')
->inject('authorization')
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Validator $redirectValidator, Document $devKey, User $user, Database $dbForProject, Reader $geodb, Event $queueForEvents, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken, Authorization $authorization) use ($oauthDefaultSuccess) {
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Validator $redirectValidator, Document $devKey, User $user, Database $dbForProject, Reader $geodb, Event $queueForEvents, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken) use ($oauthDefaultSuccess) {
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$port = $request->getPort();
$callbackBase = $protocol . '://' . $request->getHostname();
@@ -1728,7 +1724,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
]);
$user->removeAttribute('$sequence');
$userDoc = $authorization->skip(fn () => $dbForProject->createDocument('users', $user));
$userDoc = Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
$dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
@@ -1746,8 +1742,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
}
}
$authorization->addRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::users()->toString());
Authorization::setRole(Role::user($user->getId())->toString());
Authorization::setRole(Role::users()->toString());
if (false === $user->getAttribute('status')) { // Account is blocked
$failureRedirect(Exception::USER_BLOCKED); // User is in status blocked
@@ -1818,7 +1814,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$dbForProject->updateDocument('users', $user->getId(), $user);
$authorization->addRole(Role::user($user->getId())->toString());
Authorization::setRole(Role::user($user->getId())->toString());
$state['success'] = URLParser::parse($state['success']);
$query = URLParser::parseQuery($state['success']['query']);
@@ -1842,7 +1838,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'ip' => $request->getIP(),
]);
$authorization->addRole(Role::user($user->getId())->toString());
Authorization::setRole(Role::user($user->getId())->toString());
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$permissions', [
@@ -2079,8 +2075,7 @@ App::post('/v1/account/tokens/magic-url')
->inject('queueForMails')
->inject('proofForPassword')
->inject('platform')
->inject('authorization')
->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsPassword $proofForPassword, array $platform, Authorization $authorization) {
->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, User $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsPassword $proofForPassword, array $platform) {
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled');
}
@@ -2153,7 +2148,7 @@ App::post('/v1/account/tokens/magic-url')
]);
$user->removeAttribute('$sequence');
$user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user));
Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
}
$proofForToken = new ProofsToken(TOKEN_LENGTH_MAGIC_URL);
@@ -2173,7 +2168,7 @@ App::post('/v1/account/tokens/magic-url')
'ip' => $request->getIP(),
]);
$authorization->addRole(Role::user($user->getId())->toString());
Authorization::setRole(Role::user($user->getId())->toString());
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$permissions', [
@@ -2359,8 +2354,7 @@ App::post('/v1/account/tokens/email')
->inject('queueForMails')
->inject('proofForPassword')
->inject('proofForCode')
->inject('authorization')
->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, User $user, Document $project, array $platform, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsPassword $proofForPassword, ProofsCode $proofForCode, Authorization $authorization) {
->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, User $user, Document $project, array $platform, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsPassword $proofForPassword, ProofsCode $proofForCode) {
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled');
}
@@ -2429,9 +2423,9 @@ App::post('/v1/account/tokens/email')
]);
$user->removeAttribute('$sequence');
$user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user));
$user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
try {
$target = $authorization->skip(fn () => $dbForProject->createDocument('targets', new Document([
$target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
@@ -2469,7 +2463,7 @@ App::post('/v1/account/tokens/email')
'ip' => $request->getIP(),
]);
$authorization->addRole(Role::user($user->getId())->toString());
Authorization::setRole(Role::user($user->getId())->toString());
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$permissions', [
@@ -2665,11 +2659,10 @@ App::put('/v1/account/sessions/magic-url')
->inject('queueForMails')
->inject('store')
->inject('proofForCode')
->inject('authorization')
->action(function ($userId, $secret, $request, $response, $user, $dbForProject, $project, $platform, $locale, $geodb, $queueForEvents, $queueForMails, $store, $proofForCode, $authorization) use ($createSession) {
->action(function ($userId, $secret, $request, $response, $user, $dbForProject, $project, $platform, $locale, $geodb, $queueForEvents, $queueForMails, $store, $proofForCode) use ($createSession) {
$proofForToken = new ProofsToken(TOKEN_LENGTH_MAGIC_URL);
$proofForToken->setHash(new Sha());
$createSession($userId, $secret, $request, $response, $user, $dbForProject, $project, $platform, $locale, $geodb, $queueForEvents, $queueForMails, $store, $proofForToken, $proofForCode, $authorization);
$createSession($userId, $secret, $request, $response, $user, $dbForProject, $project, $platform, $locale, $geodb, $queueForEvents, $queueForMails, $store, $proofForToken, $proofForCode);
});
App::put('/v1/account/sessions/phone')
@@ -2715,7 +2708,6 @@ App::put('/v1/account/sessions/phone')
->inject('store')
->inject('proofForToken')
->inject('proofForCode')
->inject('authorization')
->action($createSession);
App::post('/v1/account/tokens/phone')
@@ -2759,8 +2751,7 @@ App::post('/v1/account/tokens/phone')
->inject('plan')
->inject('store')
->inject('proofForCode')
->inject('authorization')
->action(function (string $userId, string $phone, Request $request, Response $response, User $user, Document $project, array $platform, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Store $store, ProofsCode $proofForCode, Authorization $authorization) {
->action(function (string $userId, string $phone, Request $request, Response $response, User $user, Document $project, array $platform, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Store $store, ProofsCode $proofForCode) {
if (empty(System::getEnv('_APP_SMS_PROVIDER'))) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
@@ -2810,9 +2801,9 @@ App::post('/v1/account/tokens/phone')
]);
$user->removeAttribute('$sequence');
$user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user));
Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
try {
$target = $authorization->skip(fn () => $dbForProject->createDocument('targets', new Document([
$target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
@@ -2858,7 +2849,7 @@ App::post('/v1/account/tokens/phone')
'ip' => $request->getIP(),
]);
$authorization->addRole(Role::user($user->getId())->toString());
Authorization::setRole(Role::user($user->getId())->toString());
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$permissions', [
@@ -2963,20 +2954,22 @@ App::post('/v1/account/jwts')
],
contentType: ContentType::JSON,
))
->label('abuse-limit', 100)
->param('duration', 900, new Range(0, 3600), 'Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.', true)
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2)
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
->label('abuse-key', 'url:{url},userId:{userId}')
->inject('response')
->inject('user')
->inject('store')
->inject('proofForToken')
->action(function (Response $response, User $user, Store $store, ProofsToken $proofForToken) {
->action(function (int $duration, Response $response, User $user, Store $store, ProofsToken $proofForToken) {
$sessionId = $user->sessionVerify($store->getProperty('secret', ''), $proofForToken);
if (!$sessionId) {
throw new Exception(Exception::USER_SESSION_NOT_FOUND);
}
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 0);
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $duration, 0);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -3245,8 +3238,7 @@ App::patch('/v1/account/email')
->inject('project')
->inject('hooks')
->inject('proofForPassword')
->inject('authorization')
->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, User $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, ProofsPassword $proofForPassword, Authorization $authorization) {
->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, User $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, ProofsPassword $proofForPassword) {
// passwordUpdate will be empty if the user has never set a password
$passwordUpdate = $user->getAttribute('passwordUpdate');
@@ -3298,7 +3290,7 @@ App::patch('/v1/account/email')
->setAttribute('passwordUpdate', DateTime::now());
}
$target = $authorization->skip(fn () => $dbForProject->findOne('targets', [
$target = Authorization::skip(fn () => $dbForProject->findOne('targets', [
Query::equal('identifier', [$email]),
]));
@@ -3314,7 +3306,7 @@ App::patch('/v1/account/email')
$oldTarget = $user->find('identifier', $oldEmail, 'targets');
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
$authorization->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email)));
Authorization::skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email)));
}
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Duplicate) {
@@ -3355,9 +3347,8 @@ App::patch('/v1/account/phone')
->inject('queueForEvents')
->inject('project')
->inject('hooks')
->inject('proofForPassword')
->inject('authorization')
->action(function (string $phone, string $password, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, ProofsPassword $proofForPassword, Authorization $authorization) {
->inject('proofForPassword')
->action(function (string $phone, string $password, Response $response, User $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, ProofsPassword $proofForPassword) {
// passwordUpdate will be empty if the user has never set a password
$passwordUpdate = $user->getAttribute('passwordUpdate');
@@ -3372,7 +3363,7 @@ App::patch('/v1/account/phone')
$hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]);
$target = $authorization->skip(fn () => $dbForProject->findOne('targets', [
$target = Authorization::skip(fn () => $dbForProject->findOne('targets', [
Query::equal('identifier', [$phone]),
]));
@@ -3403,7 +3394,7 @@ App::patch('/v1/account/phone')
$oldTarget = $user->find('identifier', $oldPhone, 'targets');
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
$authorization->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone)));
Authorization::skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone)));
}
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Duplicate $th) {
@@ -3539,9 +3530,7 @@ App::post('/v1/account/recovery')
->inject('queueForMails')
->inject('queueForEvents')
->inject('proofForToken')
->inject('authorization')
->action(function (string $email, string $url, Request $request, Response $response, User $user, Database $dbForProject, Document $project, array $platform, Locale $locale, Mail $queueForMails, Event $queueForEvents, ProofsToken $proofForToken, Authorization $authorization) {
->action(function (string $email, string $url, Request $request, Response $response, User $user, Database $dbForProject, Document $project, array $platform, Locale $locale, Mail $queueForMails, Event $queueForEvents, ProofsToken $proofForToken) {
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
}
@@ -3577,7 +3566,7 @@ App::post('/v1/account/recovery')
'ip' => $request->getIP(),
]);
$authorization->addRole(Role::user($profile->getId())->toString());
Authorization::setRole(Role::user($profile->getId())->toString());
$recovery = $dbForProject->createDocument('tokens', $recovery
->setAttribute('$permissions', [
@@ -3733,8 +3722,7 @@ App::put('/v1/account/recovery')
->inject('hooks')
->inject('proofForPassword')
->inject('proofForToken')
->inject('authorization')
->action(function (string $userId, string $secret, string $password, Response $response, User $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks, ProofsPassword $proofForPassword, ProofsToken $proofForToken, Authorization $authorization) {
->action(function (string $userId, string $secret, string $password, Response $response, User $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks, ProofsPassword $proofForPassword, ProofsToken $proofForToken) {
/** @var Appwrite\Utopia\Database\Documents\User $profile */
$profile = $dbForProject->getDocument('users', $userId);
@@ -3748,7 +3736,7 @@ App::put('/v1/account/recovery')
throw new Exception(Exception::USER_INVALID_TOKEN);
}
$authorization->addRole(Role::user($profile->getId())->toString());
Authorization::setRole(Role::user($profile->getId())->toString());
$newPassword = $proofForPassword->hash($password);
@@ -3851,8 +3839,7 @@ App::post('/v1/account/verifications/email')
->inject('queueForEvents')
->inject('queueForMails')
->inject('proofForToken')
->inject('authorization')
->action(function (string $url, Request $request, Response $response, Document $project, array $platform, User $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsToken $proofForToken, Authorization $authorization) {
->action(function (string $url, Request $request, Response $response, Document $project, array $platform, User $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsToken $proofForToken) {
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
@@ -3881,7 +3868,7 @@ App::post('/v1/account/verifications/email')
'ip' => $request->getIP(),
]);
$authorization->addRole(Role::user($user->getId())->toString());
Authorization::setRole(Role::user($user->getId())->toString());
$verification = $dbForProject->createDocument('tokens', $verification
->setAttribute('$permissions', [
@@ -4080,10 +4067,9 @@ App::put('/v1/account/verifications/email')
->inject('dbForProject')
->inject('queueForEvents')
->inject('proofForToken')
->inject('authorization')
->action(function (string $userId, string $secret, Response $response, User $user, Database $dbForProject, Event $queueForEvents, ProofsToken $proofForToken, Authorization $authorization) {
->action(function (string $userId, string $secret, Response $response, User $user, Database $dbForProject, Event $queueForEvents, ProofsToken $proofForToken) {
/** @var Appwrite\Utopia\Database\Documents\User $profile */
$profile = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId));
$profile = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId));
if ($profile->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
@@ -4095,7 +4081,7 @@ App::put('/v1/account/verifications/email')
throw new Exception(Exception::USER_INVALID_TOKEN);
}
$authorization->addRole(Role::user($profile->getId())->toString());
Authorization::setRole(Role::user($profile->getId())->toString());
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('emailVerification', true));
@@ -4155,8 +4141,7 @@ App::post('/v1/account/verifications/phone')
->inject('queueForStatsUsage')
->inject('plan')
->inject('proofForCode')
->inject('authorization')
->action(function (Request $request, Response $response, User $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, ProofsCode $proofForCode, Authorization $authorization) {
->action(function (Request $request, Response $response, User $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, ProofsCode $proofForCode) {
if (empty(System::getEnv('_APP_SMS_PROVIDER'))) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
@@ -4195,7 +4180,7 @@ App::post('/v1/account/verifications/phone')
'ip' => $request->getIP(),
]);
$authorization->addRole(Role::user($user->getId())->toString());
Authorization::setRole(Role::user($user->getId())->toString());
$verification = $dbForProject->createDocument('tokens', $verification
->setAttribute('$permissions', [
@@ -4301,10 +4286,9 @@ App::put('/v1/account/verifications/phone')
->inject('dbForProject')
->inject('queueForEvents')
->inject('proofForCode')
->inject('authorization')
->action(function (string $userId, string $secret, Response $response, User $user, Database $dbForProject, Event $queueForEvents, ProofsCode $proofForCode, Authorization $authorization) {
->action(function (string $userId, string $secret, Response $response, User $user, Database $dbForProject, Event $queueForEvents, ProofsCode $proofForCode) {
/** @var Appwrite\Utopia\Database\Documents\User $profile */
$profile = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId));
$profile = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId));
if ($profile->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
@@ -4316,7 +4300,7 @@ App::put('/v1/account/verifications/phone')
throw new Exception(Exception::USER_INVALID_TOKEN);
}
$authorization->addRole(Role::user($profile->getId())->toString());
Authorization::setRole(Role::user($profile->getId())->toString());
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('phoneVerification', true));
@@ -4369,13 +4353,12 @@ App::post('/v1/account/targets/push')
->inject('dbForProject')
->inject('store')
->inject('proofForToken')
->inject('authorization')
->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, User $user, Request $request, Response $response, Database $dbForProject, Store $store, ProofsToken $proofForToken, Authorization $authorization) {
->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, User $user, Request $request, Response $response, Database $dbForProject, Store $store, ProofsToken $proofForToken) {
$targetId = $targetId == 'unique()' ? ID::unique() : $targetId;
$provider = $authorization->skip(fn () => $dbForProject->getDocument('providers', $providerId));
$provider = Authorization::skip(fn () => $dbForProject->getDocument('providers', $providerId));
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId));
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
if (!$target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
@@ -4450,10 +4433,9 @@ App::put('/v1/account/targets/:targetId/push')
->inject('request')
->inject('response')
->inject('dbForProject')
->inject('authorization')
->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) {
->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject) {
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId));
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
if ($target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
@@ -4516,9 +4498,8 @@ App::delete('/v1/account/targets/:targetId/push')
->inject('request')
->inject('response')
->inject('dbForProject')
->inject('authorization')
->action(function (string $targetId, Event $queueForEvents, Delete $queueForDeletes, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) {
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId));
->action(function (string $targetId, Event $queueForEvents, Delete $queueForDeletes, Document $user, Request $request, Response $response, Database $dbForProject) {
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
if ($target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
+13 -16
View File
@@ -70,9 +70,9 @@ $avatarCallback = function (string $type, string $code, int $width, int $height,
unset($image);
};
$getUserGitHub = function (string $userId, Document $project, Database $dbForProject, Database $dbForPlatform, Authorization $authorization, ?Logger $logger) {
$getUserGitHub = function (string $userId, Document $project, Database $dbForProject, Database $dbForPlatform, ?Logger $logger) {
try {
$user = $authorization->skip(fn () => $dbForPlatform->getDocument('users', $userId));
$user = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
$sessions = $user->getAttribute('sessions', []);
@@ -123,7 +123,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
->setAttribute('providerRefreshToken', $refreshToken)
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$oauth2->getAccessTokenExpiry('')));
$authorization->skip(fn () => $dbForProject->updateDocument('sessions', $gitHubSession->getId(), $gitHubSession));
Authorization::skip(fn () => $dbForProject->updateDocument('sessions', $gitHubSession->getId(), $gitHubSession));
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Throwable $err) {
@@ -131,7 +131,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro
do {
$previousAccessToken = $gitHubSession->getAttribute('providerAccessToken');
$user = $authorization->skip(fn () => $dbForPlatform->getDocument('users', $userId));
$user = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
$sessions = $user->getAttribute('sessions', []);
$gitHubSession = new Document();
@@ -841,9 +841,8 @@ App::get('/v1/cards/cloud')
->inject('contributors')
->inject('employees')
->inject('logger')
->inject('authorization')
->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization) use ($getUserGitHub) {
$user = $authorization->skip(fn () => $dbForPlatform->getDocument('users', $userId));
->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) {
$user = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
if ($user->isEmpty() && empty($mock)) {
throw new Exception(Exception::USER_NOT_FOUND);
@@ -854,7 +853,7 @@ App::get('/v1/cards/cloud')
$email = $user->getAttribute('email', '');
$createdAt = new \DateTime($user->getCreatedAt());
$gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $authorization, $logger);
$gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger);
$githubName = $gitHub['name'] ?? '';
$githubId = $gitHub['id'] ?? '';
@@ -1049,9 +1048,8 @@ App::get('/v1/cards/cloud-back')
->inject('contributors')
->inject('employees')
->inject('logger')
->inject('authorization')
->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization) use ($getUserGitHub) {
$user = $authorization->skip(fn () => $dbForPlatform->getDocument('users', $userId));
->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) {
$user = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
if ($user->isEmpty() && empty($mock)) {
throw new Exception(Exception::USER_NOT_FOUND);
@@ -1061,7 +1059,7 @@ App::get('/v1/cards/cloud-back')
$userId = $user->getId();
$email = $user->getAttribute('email', '');
$gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $authorization, $logger);
$gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger);
$githubId = $gitHub['id'] ?? '';
$isHero = \array_key_exists($email, $heroes);
@@ -1128,9 +1126,8 @@ App::get('/v1/cards/cloud-og')
->inject('contributors')
->inject('employees')
->inject('logger')
->inject('authorization')
->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization) use ($getUserGitHub) {
$user = $authorization->skip(fn () => $dbForPlatform->getDocument('users', $userId));
->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) {
$user = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
if ($user->isEmpty() && empty($mock)) {
throw new Exception(Exception::USER_NOT_FOUND);
@@ -1145,7 +1142,7 @@ App::get('/v1/cards/cloud-og')
$email = $user->getAttribute('email', '');
$createdAt = new \DateTime($user->getCreatedAt());
$gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $authorization, $logger);
$gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger);
$githubName = $gitHub['name'] ?? '';
$githubId = $gitHub['id'] ?? '';
+2 -3
View File
@@ -28,12 +28,11 @@ use Utopia\Validator\Text;
App::init()
->groups(['graphql'])
->inject('project')
->inject('authorization')
->action(function (Document $project, Authorization $authorization) {
->action(function (Document $project) {
if (
array_key_exists('graphql', $project->getAttribute('apis', []))
&& !$project->getAttribute('apis', [])['graphql']
&& !(User::isPrivileged($authorization->getRoles()) || User::isApp($authorization->getRoles()))
&& !(User::isPrivileged(Authorization::getRoles()) || User::isApp(Authorization::getRoles()))
) {
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
}
+29 -31
View File
@@ -120,7 +120,7 @@ App::get('/v1/health/db')
$output[] = new Document([
'name' => $key . " ($database)",
'status' => 'pass',
'ping' => \round((\microtime(true) - $checkStart) * 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000)
]);
} else {
$failures[] = $database;
@@ -131,8 +131,6 @@ App::get('/v1/health/db')
}
}
// Only throw error if ALL databases failed (no successful pings)
// This allows partial failures in environments where not all DBs are ready
if (!empty($failures)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'DB failure on: ' . implode(", ", $failures));
}
@@ -182,7 +180,7 @@ App::get('/v1/health/cache')
$output[] = new Document([
'name' => $key . " ($cache)",
'status' => 'pass',
'ping' => \round((\microtime(true) - $checkStart) * 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000)
]);
} else {
$failures[] = $cache;
@@ -242,7 +240,7 @@ App::get('/v1/health/pubsub')
$output[] = new Document([
'name' => $key . " ($pubsub)",
'status' => 'pass',
'ping' => \round((\microtime(true) - $checkStart) * 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000)
]);
} else {
$failures[] = $pubsub;
@@ -824,7 +822,7 @@ App::get('/v1/health/storage/local')
$output = [
'status' => 'pass',
'ping' => \round((\microtime(true) - $checkStart) * 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000)
];
$response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS);
@@ -876,7 +874,7 @@ App::get('/v1/health/storage')
$output = [
'status' => 'pass',
'ping' => \round((\microtime(true) - $checkStart) * 1000)
'ping' => \round((\microtime(true) - $checkStart) / 1000)
];
$response->dynamic(new Document($output), Response::MODEL_HEALTH_STATUS);
@@ -947,18 +945,18 @@ App::get('/v1/health/queue/failed/:name')
contentType: ContentType::JSON
))
->param('name', '', new WhiteList([
Event::DATABASE_QUEUE_NAME,
Event::DELETE_QUEUE_NAME,
Event::AUDITS_QUEUE_NAME,
Event::MAILS_QUEUE_NAME,
Event::FUNCTIONS_QUEUE_NAME,
Event::STATS_RESOURCES_QUEUE_NAME,
Event::STATS_USAGE_QUEUE_NAME,
Event::WEBHOOK_QUEUE_NAME,
Event::CERTIFICATES_QUEUE_NAME,
Event::BUILDS_QUEUE_NAME,
Event::MESSAGING_QUEUE_NAME,
Event::MIGRATIONS_QUEUE_NAME
System::getEnv('_APP_DATABASE_QUEUE_NAME', Event::DATABASE_QUEUE_NAME),
System::getEnv('_APP_DELETE_QUEUE_NAME', Event::DELETE_QUEUE_NAME),
System::getEnv('_APP_AUDITS_QUEUE_NAME', Event::AUDITS_QUEUE_NAME),
System::getEnv('_APP_MAILS_QUEUE_NAME', Event::MAILS_QUEUE_NAME),
System::getEnv('_APP_FUNCTIONS_QUEUE_NAME', Event::FUNCTIONS_QUEUE_NAME),
System::getEnv('_APP_STATS_RESOURCES_QUEUE_NAME', Event::STATS_RESOURCES_QUEUE_NAME),
System::getEnv('_APP_STATS_USAGE_QUEUE_NAME', Event::STATS_USAGE_QUEUE_NAME),
System::getEnv('_APP_WEBHOOK_QUEUE_NAME', Event::WEBHOOK_QUEUE_NAME),
System::getEnv('_APP_CERTIFICATES_QUEUE_NAME', Event::CERTIFICATES_QUEUE_NAME),
System::getEnv('_APP_BUILDS_QUEUE_NAME', Event::BUILDS_QUEUE_NAME),
System::getEnv('_APP_MESSAGING_QUEUE_NAME', Event::MESSAGING_QUEUE_NAME),
System::getEnv('_APP_MIGRATIONS_QUEUE_NAME', Event::MIGRATIONS_QUEUE_NAME)
]), 'The name of the queue')
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('response')
@@ -995,18 +993,18 @@ App::get('/v1/health/queue/failed/:name')
/** @var Event $queue */
$queue = match ($name) {
Event::DATABASE_QUEUE_NAME => $queueForDatabase,
Event::DELETE_QUEUE_NAME => $queueForDeletes,
Event::AUDITS_QUEUE_NAME => $queueForAudits,
Event::MAILS_QUEUE_NAME => $queueForMails,
Event::FUNCTIONS_QUEUE_NAME => $queueForFunctions,
Event::STATS_RESOURCES_QUEUE_NAME => $queueForStatsResources,
Event::STATS_USAGE_QUEUE_NAME => $queueForStatsUsage,
Event::WEBHOOK_QUEUE_NAME => $queueForWebhooks,
Event::CERTIFICATES_QUEUE_NAME => $queueForCertificates,
Event::BUILDS_QUEUE_NAME => $queueForBuilds,
Event::MESSAGING_QUEUE_NAME => $queueForMessaging,
Event::MIGRATIONS_QUEUE_NAME => $queueForMigrations,
System::getEnv('_APP_DATABASE_QUEUE_NAME', Event::DATABASE_QUEUE_NAME) => $queueForDatabase,
System::getEnv('_APP_DELETE_QUEUE_NAME', Event::DELETE_QUEUE_NAME) => $queueForDeletes,
System::getEnv('_APP_AUDITS_QUEUE_NAME', Event::AUDITS_QUEUE_NAME) => $queueForAudits,
System::getEnv('_APP_MAILS_QUEUE_NAME', Event::MAILS_QUEUE_NAME) => $queueForMails,
System::getEnv('_APP_FUNCTIONS_QUEUE_NAME', Event::FUNCTIONS_QUEUE_NAME) => $queueForFunctions,
System::getEnv('_APP_STATS_RESOURCES_QUEUE_NAME', Event::STATS_RESOURCES_QUEUE_NAME) => $queueForStatsResources,
System::getEnv('_APP_STATS_USAGE_QUEUE_NAME', Event::STATS_USAGE_QUEUE_NAME) => $queueForStatsUsage,
System::getEnv('_APP_WEBHOOK_QUEUE_NAME', Event::WEBHOOK_QUEUE_NAME) => $queueForWebhooks,
System::getEnv('_APP_CERTIFICATES_QUEUE_NAME', Event::CERTIFICATES_QUEUE_NAME) => $queueForCertificates,
System::getEnv('_APP_BUILDS_QUEUE_NAME', Event::BUILDS_QUEUE_NAME) => $queueForBuilds,
System::getEnv('_APP_MESSAGING_QUEUE_NAME', Event::MESSAGING_QUEUE_NAME) => $queueForMessaging,
System::getEnv('_APP_MIGRATIONS_QUEUE_NAME', Event::MIGRATIONS_QUEUE_NAME) => $queueForMigrations,
};
$failed = $queue->getSize(failed: true);
+30 -35
View File
@@ -36,7 +36,6 @@ use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Authorization\Input;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Queries;
use Utopia\Database\Validator\Query\Cursor;
@@ -1074,9 +1073,8 @@ App::get('/v1/messaging/providers')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('dbForProject')
->inject('authorization')
->inject('response')
->action(function (array $queries, string $search, bool $includeTotal, Database $dbForProject, Authorization $authorization, Response $response) {
->action(function (array $queries, string $search, bool $includeTotal, Database $dbForProject, Response $response) {
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
@@ -1102,7 +1100,7 @@ App::get('/v1/messaging/providers')
}
$providerId = $cursor->getValue();
$cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('providers', $providerId));
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('providers', $providerId));
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Provider '{$providerId}' for the 'cursor' value not found.");
@@ -2479,9 +2477,8 @@ App::get('/v1/messaging/topics')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('dbForProject')
->inject('authorization')
->inject('response')
->action(function (array $queries, string $search, bool $includeTotal, Database $dbForProject, Authorization $authorization, Response $response) {
->action(function (array $queries, string $search, bool $includeTotal, Database $dbForProject, Response $response) {
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
@@ -2507,7 +2504,7 @@ App::get('/v1/messaging/topics')
}
$topicId = $cursor->getValue();
$cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId));
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Topic '{$topicId}' for the 'cursor' value not found.");
@@ -2777,27 +2774,29 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
->param('targetId', '', new UID(), 'Target ID. The target ID to link to the specified Topic ID.')
->inject('queueForEvents')
->inject('dbForProject')
->inject('authorization')
->inject('response')
->action(function (string $subscriberId, string $topicId, string $targetId, Event $queueForEvents, Database $dbForProject, Authorization $authorization, Response $response) {
->action(function (string $subscriberId, string $topicId, string $targetId, Event $queueForEvents, Database $dbForProject, Response $response) {
$subscriberId = $subscriberId == 'unique()' ? ID::unique() : $subscriberId;
$topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId));
$topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
if ($topic->isEmpty()) {
throw new Exception(Exception::TOPIC_NOT_FOUND);
}
if (!$authorization->isValid(new Input('subscribe', $topic->getAttribute('subscribe')))) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
$validator = new Authorization('subscribe');
if (!$validator->isValid($topic->getAttribute('subscribe'))) {
throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription());
}
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId));
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
if ($target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
}
$user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
$user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
$subscriber = new Document([
'$id' => $subscriberId,
@@ -2830,7 +2829,7 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE),
};
$authorization->skip(fn () => $dbForProject->increaseDocumentAttribute(
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute(
'topics',
$topicId,
$totalAttribute,
@@ -2875,9 +2874,8 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('dbForProject')
->inject('authorization')
->inject('response')
->action(function (string $topicId, array $queries, string $search, bool $includeTotal, Database $dbForProject, Authorization $authorization, Response $response) {
->action(function (string $topicId, array $queries, string $search, bool $includeTotal, Database $dbForProject, Response $response) {
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
@@ -2888,7 +2886,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
$queries[] = Query::search('search', $search);
}
$topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId));
$topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
if ($topic->isEmpty()) {
throw new Exception(Exception::TOPIC_NOT_FOUND);
@@ -2911,7 +2909,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
}
$subscriberId = $cursor->getValue();
$cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('subscribers', $subscriberId));
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('subscribers', $subscriberId));
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Subscriber '{$subscriberId}' for the 'cursor' value not found.");
@@ -2925,10 +2923,10 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
}
$subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject, $authorization) {
return function () use ($subscriber, $dbForProject, $authorization) {
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId')));
$user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
$subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject) {
return function () use ($subscriber, $dbForProject) {
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId')));
$user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
return $subscriber
->setAttribute('target', $target)
@@ -3057,10 +3055,9 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->param('topicId', '', new UID(), 'Topic ID. The topic ID subscribed to.')
->param('subscriberId', '', new UID(), 'Subscriber ID.')
->inject('dbForProject')
->inject('authorization')
->inject('response')
->action(function (string $topicId, string $subscriberId, Database $dbForProject, Authorization $authorization, Response $response) {
$topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId));
->action(function (string $topicId, string $subscriberId, Database $dbForProject, Response $response) {
$topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
if ($topic->isEmpty()) {
throw new Exception(Exception::TOPIC_NOT_FOUND);
@@ -3072,8 +3069,8 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
throw new Exception(Exception::SUBSCRIBER_NOT_FOUND);
}
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId')));
$user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId')));
$user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
$subscriber
->setAttribute('target', $target)
@@ -3109,10 +3106,9 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->param('subscriberId', '', new UID(), 'Subscriber ID.')
->inject('queueForEvents')
->inject('dbForProject')
->inject('authorization')
->inject('response')
->action(function (string $topicId, string $subscriberId, Event $queueForEvents, Database $dbForProject, Authorization $authorization, Response $response) {
$topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId));
->action(function (string $topicId, string $subscriberId, Event $queueForEvents, Database $dbForProject, Response $response) {
$topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
if ($topic->isEmpty()) {
throw new Exception(Exception::TOPIC_NOT_FOUND);
@@ -3135,7 +3131,7 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE),
};
$authorization->skip(fn () => $dbForProject->decreaseDocumentAttribute(
Authorization::skip(fn () => $dbForProject->decreaseDocumentAttribute(
'topics',
$topicId,
$totalAttribute,
@@ -3694,9 +3690,8 @@ App::get('/v1/messaging/messages')
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('dbForProject')
->inject('authorization')
->inject('response')
->action(function (array $queries, string $search, bool $includeTotal, Database $dbForProject, Authorization $authorization, Response $response) {
->action(function (array $queries, string $search, bool $includeTotal, Database $dbForProject, Response $response) {
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
@@ -3722,7 +3717,7 @@ App::get('/v1/messaging/messages')
}
$messageId = $cursor->getValue();
$cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('messages', $messageId));
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('messages', $messageId));
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Message '{$messageId}' for the 'cursor' value not found.");
+5 -9
View File
@@ -342,7 +342,6 @@ App::post('/v1/migrations/csv/imports')
->inject('response')
->inject('dbForProject')
->inject('dbForPlatform')
->inject('authorization')
->inject('project')
->inject('platform')
->inject('deviceForFiles')
@@ -357,7 +356,6 @@ App::post('/v1/migrations/csv/imports')
Response $response,
Database $dbForProject,
Database $dbForPlatform,
Authorization $authorization,
Document $project,
array $platform,
Device $deviceForFiles,
@@ -365,7 +363,7 @@ App::post('/v1/migrations/csv/imports')
Event $queueForEvents,
Migration $queueForMigrations
) {
$bucket = $authorization->skip(function () use ($internalFile, $dbForPlatform, $dbForProject, $bucketId) {
$bucket = Authorization::skip(function () use ($internalFile, $dbForPlatform, $dbForProject, $bucketId) {
if ($internalFile) {
return $dbForPlatform->getDocument('buckets', 'default');
}
@@ -376,7 +374,7 @@ App::post('/v1/migrations/csv/imports')
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$file = $authorization->skip(fn () => $internalFile ? $dbForPlatform->getDocument('bucket_' . $bucket->getSequence(), $fileId) : $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = Authorization::skip(fn () => $internalFile ? $dbForPlatform->getDocument('bucket_' . $bucket->getSequence(), $fileId) : $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
@@ -493,7 +491,6 @@ App::post('/v1/migrations/csv/exports')
->inject('response')
->inject('dbForProject')
->inject('dbForPlatform')
->inject('authorization')
->inject('project')
->inject('platform')
->inject('queueForEvents')
@@ -512,7 +509,6 @@ App::post('/v1/migrations/csv/exports')
Response $response,
Database $dbForProject,
Database $dbForPlatform,
Authorization $authorization,
Document $project,
array $platform,
Event $queueForEvents,
@@ -524,7 +520,7 @@ App::post('/v1/migrations/csv/exports')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$bucket = $authorization->skip(fn () => $dbForPlatform->getDocument('buckets', 'default'));
$bucket = Authorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'default'));
if ($bucket->isEmpty()) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
@@ -537,12 +533,12 @@ App::post('/v1/migrations/csv/exports')
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
+4 -5
View File
@@ -45,10 +45,9 @@ App::get('/v1/project/usage')
->inject('response')
->inject('project')
->inject('dbForProject')
->inject('authorization')
->inject('getLogsDB')
->inject('smsRates')
->action(function (string $startDate, string $endDate, string $period, Response $response, Document $project, Database $dbForProject, Authorization $authorization, callable $getLogsDB, array $smsRates) {
->action(function (string $startDate, string $endDate, string $period, Response $response, Document $project, Database $dbForProject, callable $getLogsDB, array $smsRates) {
$stats = $total = $usage = [];
$format = 'Y-m-d 00:00:00';
$firstDay = (new DateTime($startDate))->format($format);
@@ -103,7 +102,7 @@ App::get('/v1/project/usage')
'1d' => 'Y-m-d\T00:00:00.000P',
};
$authorization->skip(function () use ($dbForProject, $dbForLogs, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) {
Authorization::skip(function () use ($dbForProject, $dbForLogs, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) {
foreach ($metrics['total'] as $metric) {
$db = ($metric === METRIC_FILES_IMAGES_TRANSFORMED) ? $dbForLogs : $dbForProject;
@@ -287,7 +286,7 @@ App::get('/v1/project/usage')
}, $dbForProject->find('functions'));
// This total is includes free and paid SMS usage
$authPhoneTotal = $authorization->skip(fn () => $dbForProject->sum('stats', 'value', [
$authPhoneTotal = Authorization::skip(fn () => $dbForProject->sum('stats', 'value', [
Query::equal('metric', [METRIC_AUTH_METHOD_PHONE]),
Query::equal('period', ['1d']),
Query::greaterThanEqual('time', $firstDay),
@@ -295,7 +294,7 @@ App::get('/v1/project/usage')
]));
// This estimate is only for paid SMS usage
$authPhoneMetrics = $authorization->skip(fn () => $dbForProject->find('stats', [
$authPhoneMetrics = Authorization::skip(fn () => $dbForProject->find('stats', [
Query::startsWith('metric', METRIC_AUTH_METHOD_PHONE . '.'),
Query::equal('period', ['1d']),
Query::greaterThanEqual('time', $firstDay),
+4 -3
View File
@@ -294,7 +294,7 @@ App::post('/v1/projects')
// Hook allowing instant project mirroring during migration
// Outside of migration, hook is not registered and has no effect
$hooks->trigger('afterProjectCreation', [ $project, $pools, $cache ]);
$hooks->trigger('afterProjectCreation', [$project, $pools, $cache]);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -2079,6 +2079,7 @@ App::patch('/v1/projects/:projectId/smtp')
if ($enabled) {
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->SMTPAuth = (!empty($username) && !empty($password));
$mail->Username = $username;
$mail->Password = $password;
$mail->Host = $host;
@@ -2094,7 +2095,7 @@ App::patch('/v1/projects/:projectId/smtp')
throw new Exception('Connection is not valid.');
}
} catch (Throwable $error) {
throw new Exception(Exception::PROJECT_SMTP_CONFIG_INVALID, 'Could not connect to SMTP server: ' . $error->getMessage());
throw new Exception(Exception::PROJECT_SMTP_CONFIG_INVALID, $error->getMessage());
}
}
@@ -2665,7 +2666,7 @@ App::patch('/v1/projects/:projectId/auth/session-invalidation')
$auths = $project->getAttribute('auths', []);
$auths['invalidateSessions'] = $enabled;
$dbForPlatform->updateDocument('projects', $project->getId(), $project
->setAttribute('auths', $auths));
->setAttribute('auths', $auths));
$response->dynamic($project, Response::MODEL_PROJECT);
});
+100 -100
View File
@@ -32,7 +32,6 @@ use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Authorization\Input;
use Utopia\Database\Validator\Permissions;
use Utopia\Database\Validator\Query\Cursor;
use Utopia\Database\Validator\UID;
@@ -434,20 +433,20 @@ App::post('/v1/storage/buckets/:bucketId/files')
->inject('mode')
->inject('deviceForFiles')
->inject('deviceForLocal')
->inject('authorization')
->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Device $deviceForFiles, Device $deviceForLocal, Authorization $authorization) {
->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Device $deviceForFiles, Device $deviceForLocal) {
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
$validator = new Authorization(Database::PERMISSION_CREATE);
if (!$validator->isValid($bucket->getCreate())) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$allowedPermissions = [
@@ -462,7 +461,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
// Add permissions for current the user if none were provided.
if (\is_null($permissions)) {
$permissions = [];
if (!empty($user->getId())) {
if (!empty($user->getId()) && !$isPrivilegedUser) {
foreach ($allowedPermissions as $permission) {
$permissions[] = (new Permission($permission, 'user', $user->getId()))->toString();
}
@@ -470,8 +469,8 @@ App::post('/v1/storage/buckets/:bucketId/files')
}
// Users can only manage their own roles, API keys and Admin users can manage any
$roles = $authorization->getRoles();
if (!User::isApp($roles) && !User::isPrivileged($roles)) {
$roles = Authorization::getRoles();
if (!$isAPIKey && !$isPrivilegedUser) {
foreach (Database::PERMISSIONS as $type) {
foreach ($permissions as $permission) {
$permission = Permission::parse($permission);
@@ -483,7 +482,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
$permission->getIdentifier(),
$permission->getDimension()
))->toString();
if (!$authorization->hasRole($role)) {
if (!Authorization::isRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')');
}
}
@@ -709,10 +708,11 @@ App::post('/v1/storage/buckets/:bucketId/files')
* However as with chunk upload even if we are updating, we are essentially creating a file
* adding it's new chunk so we validate create permission instead of update
*/
if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
$validator = new Authorization(Database::PERMISSION_CREATE);
if (!$validator->isValid($bucket->getCreate())) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file));
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file));
}
} else {
if ($file->isEmpty()) {
@@ -753,12 +753,13 @@ App::post('/v1/storage/buckets/:bucketId/files')
* However as with chunk upload even if we are updating, we are essentially creating a file
* adding it's new chunk so we validate create permission instead of update
*/
if (!$authorization->isValid(new Input(Database::PERMISSION_CREATE, $bucket->getCreate()))) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
$validator = new Authorization(Database::PERMISSION_CREATE);
if (!$validator->isValid($bucket->getCreate())) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
try {
$file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file));
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file));
} catch (NotFoundException) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
@@ -802,22 +803,22 @@ App::get('/v1/storage/buckets/:bucketId/files')
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
->inject('dbForProject')
->inject('authorization')
->inject('mode')
->action(function (string $bucketId, array $queries, string $search, bool $includeTotal, Response $response, Database $dbForProject, Authorization $authorization, string $mode) {
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
->action(function (string $bucketId, array $queries, string $search, bool $includeTotal, Response $response, Database $dbForProject, string $mode) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
if (!$fileSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$queries = Query::parseQueries($queries);
@@ -846,7 +847,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
if ($fileSecurity && !$valid) {
$cursorDocument = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
$cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
if ($cursorDocument->isEmpty()) {
@@ -856,13 +857,15 @@ App::get('/v1/storage/buckets/:bucketId/files')
$cursor->setValue($cursorDocument);
}
$filterQueries = Query::groupByType($queries)['filters'];
try {
if ($fileSecurity && !$valid) {
$files = $dbForProject->find('bucket_' . $bucket->getSequence(), $queries);
$total = $includeTotal ? $dbForProject->count('bucket_' . $bucket->getSequence(), $queries, APP_LIMIT_COUNT) : 0;
$total = $includeTotal ? $dbForProject->count('bucket_' . $bucket->getSequence(), $filterQueries, APP_LIMIT_COUNT) : 0;
} else {
$files = $authorization->skip(fn () => $dbForProject->find('bucket_' . $bucket->getSequence(), $queries));
$total = $includeTotal ? $authorization->skip(fn () => $dbForProject->count('bucket_' . $bucket->getSequence(), $queries, APP_LIMIT_COUNT)) : 0;
$files = Authorization::skip(fn () => $dbForProject->find('bucket_' . $bucket->getSequence(), $queries));
$total = $includeTotal ? Authorization::skip(fn () => $dbForProject->count('bucket_' . $bucket->getSequence(), $filterQueries, APP_LIMIT_COUNT)) : 0;
}
} catch (NotFoundException) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
@@ -901,28 +904,28 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
->param('fileId', '', new UID(), 'File ID.')
->inject('response')
->inject('dbForProject')
->inject('authorization')
->inject('mode')
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Authorization $authorization, string $mode) {
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, string $mode) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
if (!$fileSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($fileSecurity && !$valid) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
$file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
if ($file->isEmpty()) {
@@ -978,18 +981,17 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
->inject('deviceForFiles')
->inject('deviceForLocal')
->inject('project')
->inject('authorization')
->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, ?string $token, Request $request, Response $response, Database $dbForProject, Document $resourceToken, Device $deviceForFiles, Device $deviceForLocal, Document $project, Authorization $authorization) {
->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, ?string $token, Request $request, Response $response, Database $dbForProject, Document $resourceToken, Device $deviceForFiles, Device $deviceForLocal, Document $project) {
if (!\extension_loaded('imagick')) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
}
/* @type Document $bucket */
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
@@ -1001,16 +1003,17 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
if (!$fileSecurity && !$valid && !$isToken) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($fileSecurity && !$valid && !$isToken) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
/* @type Document $file */
$file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) {
@@ -1132,11 +1135,11 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
$contentType = (\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg'];
//Do not update transformedAt if it's a console user
if (!User::isPrivileged($authorization->getRoles())) {
if (!User::isPrivileged(Authorization::getRoles())) {
$transformedAt = $file->getAttribute('transformedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) {
$file->setAttribute('transformedAt', DateTime::now());
$authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file));
Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file));
}
}
@@ -1176,16 +1179,15 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
->inject('request')
->inject('response')
->inject('dbForProject')
->inject('authorization')
->inject('mode')
->inject('resourceToken')
->inject('deviceForFiles')
->action(function (string $bucketId, string $fileId, ?string $token, Request $request, Response $response, Database $dbForProject, Authorization $authorization, string $mode, Document $resourceToken, Device $deviceForFiles) {
->action(function (string $bucketId, string $fileId, ?string $token, Request $request, Response $response, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) {
/* @type Document $bucket */
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
@@ -1193,20 +1195,21 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
if (!$fileSecurity && !$valid && !$isToken) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($fileSecurity && !$valid && !$isToken) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
/* @type Document $file */
$file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($file->isEmpty()) {
@@ -1340,13 +1343,12 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
->inject('mode')
->inject('resourceToken')
->inject('deviceForFiles')
->inject('authorization')
->action(function (string $bucketId, string $fileId, ?string $token, Response $response, Request $request, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles, Authorization $authorization) {
->action(function (string $bucketId, string $fileId, ?string $token, Response $response, Request $request, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) {
/* @type Document $bucket */
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
@@ -1354,20 +1356,21 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
if (!$fileSecurity && !$valid && !$isToken) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($fileSecurity && !$valid && !$isToken) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
/* @type Document $file */
$file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($file->isEmpty()) {
@@ -1496,8 +1499,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
->inject('project')
->inject('mode')
->inject('deviceForFiles')
->inject('authorization')
->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Database $dbForPlatform, Document $project, string $mode, Device $deviceForFiles, Authorization $authorization) {
->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Database $dbForPlatform, Document $project, string $mode, Device $deviceForFiles) {
$decoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
try {
@@ -1518,15 +1520,15 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
$disposition = $decoded['disposition'] ?? 'inline';
$dbForProject = $isInternal ? $dbForPlatform : $dbForProject;
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
@@ -1534,6 +1536,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
$mimes = Config::getParam('storage-mimes');
$path = $file->getAttribute('path', '');
if (!$deviceForFiles->exists($path)) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path);
}
@@ -1670,26 +1673,26 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
->inject('user')
->inject('mode')
->inject('queueForEvents')
->inject('authorization')
->action(function (string $bucketId, string $fileId, ?string $name, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $queueForEvents, Authorization $authorization) {
->action(function (string $bucketId, string $fileId, ?string $name, ?array $permissions, Response $response, Database $dbForProject, Document $user, string $mode, Event $queueForEvents) {
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$valid = $authorization->isValid(new Input(Database::PERMISSION_UPDATE, $bucket->getUpdate()));
$validator = new Authorization(Database::PERMISSION_UPDATE);
$valid = $validator->isValid($bucket->getUpdate());
if (!$fileSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
throw new Exception(Exception::USER_UNAUTHORIZED);
}
// Read permission should not be required for update
$file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
@@ -1703,7 +1706,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
]);
// Users can only manage their own roles, API keys and Admin users can manage any
$roles = $authorization->getRoles();
$roles = Authorization::getRoles();
if (!User::isApp($roles) && !User::isPrivileged($roles) && !\is_null($permissions)) {
foreach (Database::PERMISSIONS as $type) {
foreach ($permissions as $permission) {
@@ -1716,7 +1719,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
$permission->getIdentifier(),
$permission->getDimension()
))->toString();
if (!$authorization->hasRole($role)) {
if (!Authorization::isRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')');
}
}
@@ -1737,7 +1740,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
if ($fileSecurity && !$valid) {
$file = $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file);
} else {
$file = $authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file));
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getSequence(), $fileId, $file));
}
} catch (NotFoundException) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
@@ -1785,34 +1788,33 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
->inject('mode')
->inject('deviceForFiles')
->inject('queueForDeletes')
->inject('authorization')
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceForFiles, Delete $queueForDeletes, Authorization $authorization) {
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceForFiles, Delete $queueForDeletes) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$valid = $authorization->isValid(new Input(Database::PERMISSION_DELETE, $bucket->getDelete()));
$validator = new Authorization(Database::PERMISSION_DELETE);
$valid = $validator->isValid($bucket->getDelete());
if (!$fileSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
throw new Exception(Exception::USER_UNAUTHORIZED);
}
// Read permission should not be required for delete
$file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
// Make sure we don't delete the file before the document permission check occurs
$validFile = $authorization->isValid(new Input(Database::PERMISSION_DELETE, $file->getDelete()));
if ($fileSecurity && !$valid && !$validFile) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
if ($fileSecurity && !$valid && !$validator->isValid($file->getDelete())) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
$deviceDeleted = false;
@@ -1836,7 +1838,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
if ($fileSecurity && !$valid) {
$deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
$deleted = $authorization->skip(fn () => $dbForProject->deleteDocument('bucket_' . $bucket->getSequence(), $fileId));
$deleted = Authorization::skip(fn () => $dbForProject->deleteDocument('bucket_' . $bucket->getSequence(), $fileId));
}
} catch (NotFoundException) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
@@ -1881,8 +1883,7 @@ App::get('/v1/storage/usage')
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
->inject('response')
->inject('dbForProject')
->inject('authorization')
->action(function (string $range, Response $response, Database $dbForProject, Authorization $authorization) {
->action(function (string $range, Response $response, Database $dbForProject) {
$periods = Config::getParam('usage', []);
$stats = $usage = [];
@@ -1894,7 +1895,7 @@ App::get('/v1/storage/usage')
];
$total = [];
$authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
@@ -1972,8 +1973,7 @@ App::get('/v1/storage/:bucketId/usage')
->inject('project')
->inject('dbForProject')
->inject('getLogsDB')
->inject('authorization')
->action(function (string $bucketId, string $range, Response $response, Document $project, Database $dbForProject, callable $getLogsDB, Authorization $authorization) {
->action(function (string $bucketId, string $range, Response $response, Document $project, Database $dbForProject, callable $getLogsDB) {
$dbForLogs = call_user_func($getLogsDB, $project);
$bucket = $dbForProject->getDocument('buckets', $bucketId);
@@ -1991,7 +1991,7 @@ App::get('/v1/storage/:bucketId/usage')
str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED),
];
$authorization->skip(function () use ($dbForProject, $dbForLogs, $bucket, $days, $metrics, &$stats) {
Authorization::skip(function () use ($dbForProject, $dbForLogs, $bucket, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$db = ($metric === str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED))
? $dbForLogs
+28 -35
View File
@@ -86,17 +86,16 @@ App::post('/v1/teams')
->inject('response')
->inject('user')
->inject('dbForProject')
->inject('authorization')
->inject('queueForEvents')
->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Authorization $authorization, Event $queueForEvents) {
->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAppUser = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$isAppUser = User::isApp(Authorization::getRoles());
$teamId = $teamId == 'unique()' ? ID::unique() : $teamId;
try {
$team = $authorization->skip(fn () => $dbForProject->createDocument('teams', new Document([
$team = Authorization::skip(fn () => $dbForProject->createDocument('teams', new Document([
'$id' => $teamId,
'$permissions' => [
Permission::read(Role::team($teamId)),
@@ -492,7 +491,6 @@ App::post('/v1/teams/:teamId/memberships')
->inject('project')
->inject('user')
->inject('dbForProject')
->inject('authorization')
->inject('locale')
->inject('queueForMails')
->inject('queueForMessaging')
@@ -502,9 +500,9 @@ App::post('/v1/teams/:teamId/memberships')
->inject('plan')
->inject('proofForPassword')
->inject('proofForToken')
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Authorization $authorization, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Password $proofForPassword, Token $proofForToken) {
$isAppUser = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Password $proofForPassword, Token $proofForToken) {
$isAppUser = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$url = htmlentities($url);
if (empty($url)) {
@@ -621,13 +619,13 @@ App::post('/v1/teams/:teamId/memberships')
]);
try {
$invitee = $authorization->skip(fn () => $dbForProject->createDocument('users', $userDocument));
$invitee = Authorization::skip(fn () => $dbForProject->createDocument('users', $userDocument));
} catch (Duplicate $th) {
throw new Exception(Exception::USER_ALREADY_EXISTS);
}
}
$isOwner = $authorization->hasRole('team:' . $team->getId() . '/owner');
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');
if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server)
throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to send invitations for this team');
@@ -663,11 +661,11 @@ App::post('/v1/teams/:teamId/memberships')
]);
$membership = ($isPrivilegedUser || $isAppUser) ?
$authorization->skip(fn () => $dbForProject->createDocument('memberships', $membership)) :
Authorization::skip(fn () => $dbForProject->createDocument('memberships', $membership)) :
$dbForProject->createDocument('memberships', $membership);
if ($isPrivilegedUser || $isAppUser) {
$authorization->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
}
} elseif ($membership->getAttribute('confirm') === false) {
$membership->setAttribute('secret', $proofForToken->hash($secret));
@@ -679,7 +677,7 @@ App::post('/v1/teams/:teamId/memberships')
}
$membership = ($isPrivilegedUser || $isAppUser) ?
$authorization->skip(fn () => $dbForProject->updateDocument('memberships', $membership->getId(), $membership)) :
Authorization::skip(fn () => $dbForProject->updateDocument('memberships', $membership->getId(), $membership)) :
$dbForProject->updateDocument('memberships', $membership->getId(), $membership);
} else {
throw new Exception(Exception::MEMBERSHIP_ALREADY_CONFIRMED);
@@ -865,8 +863,7 @@ App::get('/v1/teams/:teamId/memberships')
->inject('response')
->inject('project')
->inject('dbForProject')
->inject('authorization')
->action(function (string $teamId, array $queries, string $search, bool $includeTotal, Response $response, Document $project, Database $dbForProject, Authorization $authorization) {
->action(function (string $teamId, array $queries, string $search, bool $includeTotal, Response $response, Document $project, Database $dbForProject) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -936,7 +933,7 @@ App::get('/v1/teams/:teamId/memberships')
'mfa' => $project->getAttribute('auths', [])['membershipsMfa'] ?? true,
];
$roles = $authorization->getRoles();
$roles = Authorization::getRoles();
$isPrivilegedUser = User::isPrivileged($roles);
$isAppUser = User::isApp($roles);
@@ -1007,8 +1004,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
->inject('response')
->inject('project')
->inject('dbForProject')
->inject('authorization')
->action(function (string $teamId, string $membershipId, Response $response, Document $project, Database $dbForProject, Authorization $authorization) {
->action(function (string $teamId, string $membershipId, Response $response, Document $project, Database $dbForProject) {
$team = $dbForProject->getDocument('teams', $teamId);
@@ -1028,7 +1024,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
'mfa' => $project->getAttribute('auths', [])['membershipsMfa'] ?? true,
];
$roles = $authorization->getRoles();
$roles = Authorization::getRoles();
$isPrivilegedUser = User::isPrivileged($roles);
$isAppUser = User::isApp($roles);
@@ -1107,9 +1103,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
->inject('user')
->inject('project')
->inject('dbForProject')
->inject('authorization')
->inject('queueForEvents')
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Authorization $authorization, Event $queueForEvents) {
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -1126,9 +1121,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
throw new Exception(Exception::USER_NOT_FOUND);
}
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAppUser = User::isApp($authorization->getRoles());
$isOwner = $authorization->hasRole('team:' . $team->getId() . '/owner');
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$isAppUser = User::isApp(Authorization::getRoles());
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');
if ($project->getId() === 'console') {
// Quick check: fetch up to 2 owners to determine if only one exists
@@ -1209,13 +1204,12 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->inject('response')
->inject('user')
->inject('dbForProject')
->inject('authorization')
->inject('project')
->inject('geodb')
->inject('queueForEvents')
->inject('store')
->inject('proofForToken')
->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Authorization $authorization, $project, Reader $geodb, Event $queueForEvents, Store $store, Token $proofForToken) {
->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents, Store $store, Token $proofForToken) {
$protocol = $request->getProtocol();
$membership = $dbForProject->getDocument('memberships', $membershipId);
@@ -1224,7 +1218,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
throw new Exception(Exception::MEMBERSHIP_NOT_FOUND);
}
$team = $authorization->skip(fn () => $dbForProject->getDocument('teams', $teamId));
$team = Authorization::skip(fn () => $dbForProject->getDocument('teams', $teamId));
if ($team->isEmpty()) {
throw new Exception(Exception::TEAM_NOT_FOUND);
@@ -1260,11 +1254,11 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->setAttribute('confirm', true)
;
$authorization->skip(fn () => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true)));
Authorization::skip(fn () => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true)));
// Create session for the user if not logged in
if (!$hasSession) {
$authorization->addRole(Role::user($user->getId())->toString());
Authorization::setRole(Role::user($user->getId())->toString());
$detector = new Detector($request->getUserAgent('UNKNOWN'));
$record = $geodb->get($request->getIP());
@@ -1292,7 +1286,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
$session = $dbForProject->createDocument('sessions', $session);
$authorization->addRole(Role::user($userId)->toString());
Authorization::setRole(Role::user($userId)->toString());
$encoded = $store
->setProperty('id', $user->getId())
@@ -1330,7 +1324,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
$dbForProject->purgeCachedDocument('users', $user->getId());
$authorization->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
$queueForEvents
->setParam('userId', $user->getId())
@@ -1374,9 +1368,8 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
->inject('project')
->inject('response')
->inject('dbForProject')
->inject('authorization')
->inject('queueForEvents')
->action(function (string $teamId, string $membershipId, Document $user, Document $project, Response $response, Database $dbForProject, Authorization $authorization, Event $queueForEvents) {
->action(function (string $teamId, string $membershipId, Document $user, Document $project, Response $response, Database $dbForProject, Event $queueForEvents) {
$membership = $dbForProject->getDocument('memberships', $membershipId);
@@ -1434,7 +1427,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
$dbForProject->purgeCachedDocument('users', $profile->getId());
if ($membership->getAttribute('confirm')) { // Count only confirmed members
$authorization->skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0));
Authorization::skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0));
}
$queueForEvents
+3 -3
View File
@@ -2674,8 +2674,8 @@ App::get('/v1/users/usage')
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
->inject('response')
->inject('dbForProject')
->inject('authorization')
->action(function (string $range, Response $response, Database $dbForProject, Authorization $authorization) {
->inject('register')
->action(function (string $range, Response $response, Database $dbForProject) {
$periods = Config::getParam('usage', []);
$stats = $usage = [];
@@ -2685,7 +2685,7 @@ App::get('/v1/users/usage')
METRIC_SESSIONS,
];
$authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) {
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $count => $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
+29 -31
View File
@@ -76,7 +76,7 @@ use Utopia\VCS\Exception\RepositoryNotFound;
use function Swoole\Coroutine\batch;
$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForPlatform, Authorization $authorization, Build $queueForBuilds, callable $getProjectDB, Request $request, array $platform) {
$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForPlatform, Build $queueForBuilds, callable $getProjectDB, array $platform) {
$errors = [];
foreach ($repositories as $repository) {
try {
@@ -87,12 +87,12 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
}
$projectId = $repository->getAttribute('projectId');
$project = $authorization->skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
$dbForProject = $getProjectDB($project);
$resourceCollection = $resourceType === "function" ? 'functions' : 'sites';
$resourceId = $repository->getAttribute('resourceId');
$resource = $authorization->skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId));
$resource = Authorization::skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId));
$resourceInternalId = $resource->getSequence();
$deploymentId = ID::unique();
@@ -141,7 +141,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$latestCommentId = '';
if (!empty($providerPullRequestId) && $resource->getAttribute('providerSilentMode', false) === false) {
$latestComment = $authorization->skip(fn () => $dbForPlatform->findOne('vcsComments', [
$latestComment = Authorization::skip(fn () => $dbForPlatform->findOne('vcsComments', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::equal('providerPullRequestId', [$providerPullRequestId]),
Query::orderDesc('$createdAt'),
@@ -180,7 +180,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment()));
} finally {
$authorization->skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId));
Authorization::skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId));
}
}
} else {
@@ -191,7 +191,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
if (!empty($latestCommentId)) {
$teamId = $project->getAttribute('teamId', '');
$latestComment = $authorization->skip(fn () => $dbForPlatform->createDocument('vcsComments', new Document([
$latestComment = Authorization::skip(fn () => $dbForPlatform->createDocument('vcsComments', new Document([
'$id' => ID::unique(),
'$permissions' => [
Permission::read(Role::team(ID::custom($teamId))),
@@ -212,7 +212,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
}
}
} elseif (!empty($providerBranch)) {
$latestComments = $authorization->skip(fn () => $dbForPlatform->find('vcsComments', [
$latestComments = Authorization::skip(fn () => $dbForPlatform->find('vcsComments', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::equal('providerBranch', [$providerBranch]),
Query::orderDesc('$createdAt'),
@@ -251,7 +251,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment()));
} finally {
$authorization->skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId));
Authorization::skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId));
}
}
}
@@ -294,7 +294,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$commands[] = $resource->getAttribute('commands', '');
}
$deployment = $authorization->skip(fn () => $dbForProject->createDocument('deployments', new Document([
$deployment = Authorization::skip(fn () => $dbForProject->createDocument('deployments', new Document([
'$id' => $deploymentId,
'$permissions' => [
Permission::read(Role::any()),
@@ -334,7 +334,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
->setAttribute('latestDeploymentInternalId', $deployment->getSequence())
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
$authorization->skip(fn () => $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource));
Authorization::skip(fn () => $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource));
if ($resource->getCollection() === 'sites') {
$projectId = $project->getId();
@@ -344,7 +344,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$domain = ID::unique() . "." . $sitesDomain;
$ruleId = md5($domain);
$previewRuleId = $ruleId;
$authorization->skip(
Authorization::skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -377,7 +377,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$domain = "branch-{$branchPrefix}-{$resourceProjectHash}.{$sitesDomain}";
$ruleId = md5($domain);
try {
$authorization->skip(
Authorization::skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -408,7 +408,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$domain = "commit-" . substr($providerCommitHash, 0, 16) . ".{$sitesDomain}";
$ruleId = md5($domain);
try {
$authorization->skip(
Authorization::skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -460,7 +460,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
if ($lockAcquired) {
// Wrap in try/finally to ensure lock file gets deleted
try {
$rule = $authorization->skip(fn () => $dbForPlatform->getDocument('rules', $previewRuleId));
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', $previewRuleId));
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$previewUrl = !empty($rule) ? ("{$protocol}://" . $rule->getAttribute('domain', '')) : '';
@@ -472,7 +472,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment());
}
} finally {
$authorization->skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId));
Authorization::skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId));
}
}
}
@@ -1476,12 +1476,11 @@ App::post('/v1/vcs/github/events')
->inject('request')
->inject('response')
->inject('dbForPlatform')
->inject('authorization')
->inject('getProjectDB')
->inject('queueForBuilds')
->inject('platform')
->action(
function (GitHub $github, Request $request, Response $response, Database $dbForPlatform, Authorization $authorization, callable $getProjectDB, Build $queueForBuilds, array $platform) use ($createGitDeployments) {
function (GitHub $github, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Build $queueForBuilds, array $platform) use ($createGitDeployments) {
$payload = $request->getRawPayload();
$signatureRemote = $request->getHeader('x-hub-signature-256', '');
$signatureLocal = System::getEnv('_APP_VCS_GITHUB_WEBHOOK_SECRET', '');
@@ -1517,14 +1516,14 @@ App::post('/v1/vcs/github/events')
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
//find resourceId from relevant resources table
$repositories = $authorization->skip(fn () => $dbForPlatform->find('repositories', [
$repositories = Authorization::skip(fn () => $dbForPlatform->find('repositories', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::limit(100),
]));
// create new deployment only on push (not committed by us) and not when branch is created or deleted
if ($providerCommitAuthorEmail !== APP_VCS_GITHUB_EMAIL && !$providerBranchCreated && !$providerBranchDeleted) {
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthorName, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForPlatform, $authorization, $queueForBuilds, $getProjectDB, $request, $platform);
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthorName, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForPlatform, $queueForBuilds, $getProjectDB, $platform);
}
} elseif ($event == $github::EVENT_INSTALLATION) {
if ($parsedPayload["action"] == "deleted") {
@@ -1537,16 +1536,16 @@ App::post('/v1/vcs/github/events')
]);
foreach ($installations as $installation) {
$repositories = $authorization->skip(fn () => $dbForPlatform->find('repositories', [
$repositories = Authorization::skip(fn () => $dbForPlatform->find('repositories', [
Query::equal('installationInternalId', [$installation->getSequence()]),
Query::limit(1000)
]));
foreach ($repositories as $repository) {
$authorization->skip(fn () => $dbForPlatform->deleteDocument('repositories', $repository->getId()));
Authorization::skip(fn () => $dbForPlatform->deleteDocument('repositories', $repository->getId()));
}
$authorization->skip(fn () => $dbForPlatform->deleteDocument('installations', $installation->getId()));
Authorization::skip(fn () => $dbForPlatform->deleteDocument('installations', $installation->getId()));
}
}
} elseif ($event == $github::EVENT_PULL_REQUEST) {
@@ -1575,12 +1574,12 @@ App::post('/v1/vcs/github/events')
$providerCommitAuthor = $commitDetails["commitAuthor"] ?? '';
$providerCommitMessage = $commitDetails["commitMessage"] ?? '';
$repositories = $authorization->skip(fn () => $dbForPlatform->find('repositories', [
$repositories = Authorization::skip(fn () => $dbForPlatform->find('repositories', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::orderDesc('$createdAt')
]));
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForPlatform, $authorization, $queueForBuilds, $getProjectDB, $request, $platform);
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForPlatform, $queueForBuilds, $getProjectDB, $platform);
} elseif ($parsedPayload["action"] == "closed") {
// Allowed external contributions cleanup
@@ -1589,7 +1588,7 @@ App::post('/v1/vcs/github/events')
$external = $parsedPayload["external"] ?? true;
if ($external) {
$repositories = $authorization->skip(fn () => $dbForPlatform->find('repositories', [
$repositories = Authorization::skip(fn () => $dbForPlatform->find('repositories', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::orderDesc('$createdAt')
]));
@@ -1600,7 +1599,7 @@ App::post('/v1/vcs/github/events')
if (\in_array($providerPullRequestId, $providerPullRequestIds)) {
$providerPullRequestIds = \array_diff($providerPullRequestIds, [$providerPullRequestId]);
$repository = $repository->setAttribute('providerPullRequestIds', $providerPullRequestIds);
$repository = $authorization->skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository));
$repository = Authorization::skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository));
}
}
}
@@ -1787,18 +1786,17 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor
->inject('response')
->inject('project')
->inject('dbForPlatform')
->inject('authorization')
->inject('getProjectDB')
->inject('queueForBuilds')
->inject('platform')
->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Request $request, Response $response, Document $project, Database $dbForPlatform, Authorization $authorization, callable $getProjectDB, Build $queueForBuilds, array $platform) use ($createGitDeployments) {
->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Response $response, Document $project, Database $dbForPlatform, callable $getProjectDB, Build $queueForBuilds, array $platform) use ($createGitDeployments) {
$installation = $dbForPlatform->getDocument('installations', $installationId);
if ($installation->isEmpty()) {
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
}
$repository = $authorization->skip(fn () => $dbForPlatform->findOne('repositories', [
$repository = Authorization::skip(fn () => $dbForPlatform->findOne('repositories', [
Query::equal('$id', [$repositoryId]),
Query::equal('projectInternalId', [$project->getSequence()])
]));
@@ -1816,7 +1814,7 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor
// TODO: Delete from array when PR is closed
$repository = $authorization->skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository));
$repository = Authorization::skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository));
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
@@ -1848,7 +1846,7 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor
$providerCommitMessage = $pullRequestResponse['title'] ?? '';
$providerCommitUrl = $pullRequestResponse['html_url'] ?? '';
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, '', '', '', '', $providerCommitHash, '', '', '', '', $providerPullRequestId, true, $dbForPlatform, $authorization, $queueForBuilds, $getProjectDB, $request, $platform);
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, true, $dbForPlatform, $queueForBuilds, $getProjectDB, $platform);
$response->noContent();
});
+37 -47
View File
@@ -59,7 +59,7 @@ Config::setParam('domainVerification', false);
Config::setParam('cookieDomain', 'localhost');
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, Authorization $authorization, ?Key $apiKey)
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey)
{
$host = $request->getHostname() ?? '';
if (!empty($previewHostname)) {
@@ -67,16 +67,16 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
}
// TODO: (@Meldiron) Remove after 1.7.x migration
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
$rule = $authorization->skip(fn () => $dbForPlatform->getDocument('rules', md5($host)));
} else {
$rule = $authorization->skip(
fn () => $dbForPlatform->find('rules', [
Query::equal('domain', [$host]),
Query::limit(1)
])
)[0] ?? new Document();
}
$isMd5 = System::getEnv('_APP_RULES_FORMAT') === 'md5';
$rule = Authorization::skip(function () use ($dbForPlatform, $host, $isMd5) {
if ($isMd5) {
return $dbForPlatform->getDocument('rules', md5($host));
}
return $dbForPlatform->findOne('rules', [
Query::equal('domain', [$host]),
]) ?? new Document();
});
$errorView = __DIR__ . '/../views/general/error.phtml';
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
@@ -111,7 +111,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
}
$projectId = $rule->getAttribute('projectId');
$project = $authorization->skip(
$project = Authorization::skip(
fn () => $dbForPlatform->getDocument('projects', $projectId)
);
@@ -119,7 +119,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
$accessedAt = $project->getAttribute('accessedAt', 0);
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
$project->setAttribute('accessedAt', DateTime::now());
$authorization->skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
Authorization::skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
}
/**
@@ -158,7 +158,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
/** @var Document $deployment */
if (!empty($rule->getAttribute('deploymentId', ''))) {
$deployment = $authorization->skip(fn () => $dbForProject->getDocument('deployments', $rule->getAttribute('deploymentId')));
$deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $rule->getAttribute('deploymentId')));
} else {
// 1.6.x DB schema compatibility
// TODO: Make sure deploymentId is never empty, and remove this code
@@ -172,15 +172,15 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
// Document of site or function
$resource = $resourceType === 'function' ?
$authorization->skip(fn () => $dbForProject->getDocument('functions', $resourceId)) :
$authorization->skip(fn () => $dbForProject->getDocument('sites', $resourceId));
Authorization::skip(fn () => $dbForProject->getDocument('functions', $resourceId)) :
Authorization::skip(fn () => $dbForProject->getDocument('sites', $resourceId));
// ID of active deployments
// Attempts to use attribute from both schemas (1.6 and 1.7)
$activeDeploymentId = $resource->getAttribute('deploymentId', $resource->getAttribute('deployment', ''));
// Get deployment document, as intended originally
$deployment = $authorization->skip(fn () => $dbForProject->getDocument('deployments', $activeDeploymentId));
$deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $activeDeploymentId));
}
if ($deployment->getAttribute('resourceType', '') === 'functions') {
@@ -199,8 +199,8 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
}
$resource = $type === 'function' ?
$authorization->skip(fn () => $dbForProject->getDocument('functions', $deployment->getAttribute('resourceId', ''))) :
$authorization->skip(fn () => $dbForProject->getDocument('sites', $deployment->getAttribute('resourceId', '')));
Authorization::skip(fn () => $dbForProject->getDocument('functions', $deployment->getAttribute('resourceId', ''))) :
Authorization::skip(fn () => $dbForProject->getDocument('sites', $deployment->getAttribute('resourceId', '')));
$isPreview = $type === 'function' ? false : ($rule->getAttribute('trigger', '') !== 'manual');
@@ -242,7 +242,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
$userExists = false;
$userId = $payload['userId'] ?? '';
if (!empty($userId)) {
$user = $authorization->skip(fn () => $dbForPlatform->getDocument('users', $userId));
$user = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
if (!$user->isEmpty() && $user->getAttribute('status', false)) {
$userExists = true;
}
@@ -255,7 +255,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
}
$membershipExists = false;
$project = $authorization->skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
if (!$project->isEmpty() && isset($user)) {
$teamId = $project->getAttribute('teamId', '');
$membership = $user->find('teamId', $teamId, 'memberships');
@@ -862,16 +862,15 @@ App::init()
->inject('devKey')
->inject('apiKey')
->inject('cors')
->inject('authorization')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Func $queueForFunctions, Executor $executor, array $platform, callable $isResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey, Cors $cors, Authorization $authorization) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Func $queueForFunctions, Executor $executor, array $platform, callable $isResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey, Cors $cors) {
/*
* Appwrite Router
*/
$hostname = $request->getHostname() ?? '';
$platformHostnames = $platform['hostnames'] ?? [];
// Only run Router when external domain
if (!\in_array($hostname, $platformHostnames) || !empty($previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey)) {
if (!in_array($hostname, $platformHostnames) || !empty($previewHostname)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $apiKey)) {
$utopia->getRoute()?->label('router', true);
}
}
@@ -1034,8 +1033,7 @@ App::init()
->inject('dbForPlatform')
->inject('queueForCertificates')
->inject('platform')
->inject('authorization')
->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $platform, Authorization $authorization) {
->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $platform) {
$hostname = $request->getHostname();
$cache = Config::getParam('hostnames', []);
$platformHostnames = $platform['hostnames'] ?? [];
@@ -1050,23 +1048,20 @@ App::init()
if (empty($domain->get()) || !$domain->isKnown() || $domain->isTest()) {
$cache[$domain->get()] = false;
Config::setParam('hostnames', $cache);
Console::warning($domain->get() . ' is not a publicly accessible domain. Skipping SSL certificate generation.');
return;
}
if (str_starts_with($request->getURI(), '/.well-known/acme-challenge')) {
Console::warning('Skipping SSL certificates generation on ACME challenge.');
return;
}
// 3. Check if domain is a main domain
if (!in_array($domain->get(), $platformHostnames)) {
Console::warning($domain->get() . ' is not a main domain. Skipping SSL certificate generation.');
return;
}
// 4. Check/create rule (requires DB access)
$authorization->disable();
Authorization::disable();
try {
// TODO: (@Meldiron) Remove after 1.7.x migration
$isMd5 = System::getEnv('_APP_RULES_FORMAT') === 'md5';
@@ -1122,7 +1117,7 @@ App::init()
} finally {
$cache[$domain->get()] = true;
Config::setParam('hostnames', $cache);
$authorization->reset();
Authorization::reset();
}
});
@@ -1146,8 +1141,7 @@ App::options()
->inject('devKey')
->inject('apiKey')
->inject('cors')
->inject('authorization')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, Document $project, Document $devKey, ?Key $apiKey, Cors $cors, Authorization $authorization) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, Document $project, Document $devKey, ?Key $apiKey, Cors $cors) {
/*
* Appwrite Router
*/
@@ -1188,8 +1182,7 @@ App::error()
->inject('log')
->inject('queueForStatsUsage')
->inject('devKey')
->inject('authorization')
->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, StatsUsage $queueForStatsUsage, Document $devKey, Authorization $authorization) {
->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, StatsUsage $queueForStatsUsage) {
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
$route = $utopia->getRoute();
$class = \get_class($error);
@@ -1271,7 +1264,7 @@ App::error()
* If not a publishable error, track usage stats. Publishable errors are >= 500 or those explicitly marked as publish=true in errors.php
*/
if (!$publish && $project->getId() !== 'console') {
if (!DBUser::isPrivileged($authorization->getRoles())) {
if (!DBUser::isPrivileged(Authorization::getRoles())) {
$fileSize = 0;
$file = $request->getFiles('file');
if (!empty($file)) {
@@ -1333,7 +1326,7 @@ App::error()
$log->addExtra('file', $error->getFile());
$log->addExtra('line', $error->getLine());
$log->addExtra('trace', $error->getTraceAsString());
$log->addExtra('roles', $authorization->getRoles());
$log->addExtra('roles', Authorization::getRoles());
$action = 'UNKNOWN_NAMESPACE.UNKNOWN.METHOD';
if (!empty($sdk)) {
@@ -1457,14 +1450,13 @@ App::get('/robots.txt')
->inject('platform')
->inject('previewHostname')
->inject('apiKey')
->inject('authorization')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey, Authorization $authorization) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey) {
$platformHostnames = $platform['hostnames'] ?? [];
if (in_array($request->getHostname(), $platformHostnames) || !empty($previewHostname)) {
$template = new View(__DIR__ . '/../views/general/robots.phtml');
$response->text($template->render(false));
} else {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $apiKey)) {
$utopia->getRoute()?->label('router', true);
}
}
@@ -1490,14 +1482,13 @@ App::get('/humans.txt')
->inject('platform')
->inject('previewHostname')
->inject('apiKey')
->inject('authorization')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey, Authorization $authorization) {
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey) {
$platformHostnames = $platform['hostnames'] ?? [];
if (in_array($request->getHostname(), $platformHostnames) || !empty($previewHostname)) {
$template = new View(__DIR__ . '/../views/general/humans.phtml');
$response->text($template->render(false));
} else {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $apiKey)) {
$utopia->getRoute()?->label('router', true);
}
}
@@ -1581,8 +1572,7 @@ App::get('/v1/ping')
->inject('project')
->inject('dbForPlatform')
->inject('queueForEvents')
->inject('authorization')
->action(function (Response $response, Document $project, Database $dbForPlatform, Event $queueForEvents, Authorization $authorization) {
->action(function (Response $response, Document $project, Database $dbForPlatform, Event $queueForEvents) {
if ($project->isEmpty() || $project->getId() === 'console') {
throw new AppwriteException(AppwriteException::PROJECT_NOT_FOUND);
}
@@ -1594,7 +1584,7 @@ App::get('/v1/ping')
->setAttribute('pingCount', $pingCount)
->setAttribute('pingedAt', $pingedAt);
$authorization->skip(function () use ($dbForPlatform, $project) {
Authorization::skip(function () use ($dbForPlatform, $project) {
$dbForPlatform->updateDocument('projects', $project->getId(), $project);
});
+25 -28
View File
@@ -30,7 +30,6 @@ use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Authorization\Input;
use Utopia\Queue\Publisher;
use Utopia\System\System;
use Utopia\Telemetry\Adapter as Telemetry;
@@ -234,8 +233,7 @@ App::init()
->inject('mode')
->inject('team')
->inject('apiKey')
->inject('authorization')
->action(function (App $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $apiKey, Authorization $authorization) {
->action(function (App $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, User $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $apiKey) {
$route = $utopia->getRoute();
/**
@@ -320,7 +318,7 @@ App::init()
// Handle special app role case
if ($apiKey->getRole() === User::ROLE_APPS) {
// Disable authorization checks for API keys
$authorization->setDefaultStatus(false);
Authorization::setDefaultStatus(false);
$user = new User([
'$id' => '',
@@ -394,14 +392,14 @@ App::init()
$scopes = \array_merge($scopes, $roles[$role]['scopes']);
}
$authorization->setDefaultStatus(false); // Cancel security segmentation for admin users.
Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
}
$scopes = \array_unique($scopes);
$authorization->addRole($role);
foreach ($user->getRoles($authorization) as $authRole) {
$authorization->addRole($authRole);
Authorization::setRole($role);
foreach ($user->getRoles() as $authRole) {
Authorization::setRole($authRole);
}
// Step 6: Update project and user last activity
@@ -409,7 +407,7 @@ App::init()
$accessedAt = $project->getAttribute('accessedAt', 0);
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
$project->setAttribute('accessedAt', DateTime::now());
$authorization->skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
Authorization::skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
}
}
@@ -444,7 +442,7 @@ App::init()
if (
array_key_exists($namespace, $project->getAttribute('services', []))
&& !$project->getAttribute('services', [])[$namespace]
&& !(User::isPrivileged($authorization->getRoles()) || User::isApp($authorization->getRoles()))
&& !(User::isPrivileged(Authorization::getRoles()) || User::isApp(Authorization::getRoles()))
) {
throw new Exception(Exception::GENERAL_SERVICE_DISABLED);
}
@@ -511,15 +509,14 @@ App::init()
->inject('devKey')
->inject('telemetry')
->inject('platform')
->inject('authorization')
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Publisher $publisherFunctions, Publisher $publisherWebhooks, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Mail $queueForMails, Migration $queueForMigrations, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry, array $platform, Authorization $authorization) use ($usageDatabaseListener, $eventDatabaseListener) {
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Publisher $publisherFunctions, Publisher $publisherWebhooks, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Mail $queueForMails, Migration $queueForMigrations, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry, array $platform) use ($usageDatabaseListener, $eventDatabaseListener) {
$route = $utopia->getRoute();
if (
array_key_exists('rest', $project->getAttribute('apis', []))
&& !$project->getAttribute('apis', [])['rest']
&& !(User::isPrivileged($authorization->getRoles()) || User::isApp($authorization->getRoles()))
&& !(User::isPrivileged(Authorization::getRoles()) || User::isApp(Authorization::getRoles()))
) {
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
}
@@ -549,7 +546,7 @@ App::init()
$closestLimit = null;
$roles = $authorization->getRoles();
$roles = Authorization::getRoles();
$isPrivilegedUser = User::isPrivileged($roles);
$isAppUser = User::isApp($roles);
@@ -660,10 +657,10 @@ App::init()
if ($useCache) {
$route = $utopia->match($request);
$isImageTransformation = $route->getPath() === '/v1/storage/buckets/:bucketId/files/:fileId/preview';
$isDisabled = isset($plan['imageTransformations']) && $plan['imageTransformations'] === -1 && !User::isPrivileged($authorization->getRoles());
$isDisabled = isset($plan['imageTransformations']) && $plan['imageTransformations'] === -1 && !User::isPrivileged(Authorization::getRoles());
$key = $request->cacheIdentifier();
$cacheLog = $authorization->skip(fn () => $dbForProject->getDocument('cache', $key));
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
);
@@ -680,10 +677,10 @@ App::init()
if ($type === 'bucket' && (!$isImageTransformation || !$isDisabled)) {
$bucketId = $parts[1] ?? null;
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAppUser && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
@@ -694,7 +691,8 @@ App::init()
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
if (!$fileSecurity && !$valid && !$isToken) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
@@ -705,7 +703,7 @@ App::init()
if ($fileSecurity && !$valid && !$isToken) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
$file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) {
@@ -716,11 +714,11 @@ App::init()
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
//Do not update transformedAt if it's a console user
if (!User::isPrivileged($authorization->getRoles())) {
if (!User::isPrivileged(Authorization::getRoles())) {
$transformedAt = $file->getAttribute('transformedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) {
$file->setAttribute('transformedAt', DateTime::now());
$authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file));
Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file));
}
}
}
@@ -816,8 +814,7 @@ App::shutdown()
->inject('queueForWebhooks')
->inject('queueForRealtime')
->inject('dbForProject')
->inject('authorization')
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, StatsUsage $queueForStatsUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject, Authorization $authorization) use ($parseLabel) {
->action(function (App $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, Audit $queueForAudits, StatsUsage $queueForStatsUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject) use ($parseLabel) {
$responsePayload = $response->getPayload();
@@ -943,11 +940,11 @@ App::shutdown()
$key = $request->cacheIdentifier();
$signature = md5($data['payload']);
$cacheLog = $authorization->skip(fn () => $dbForProject->getDocument('cache', $key));
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$accessedAt = $cacheLog->getAttribute('accessedAt', 0);
$now = DateTime::now();
if ($cacheLog->isEmpty()) {
$authorization->skip(fn () => $dbForProject->createDocument('cache', new Document([
Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([
'$id' => $key,
'resource' => $resource,
'resourceType' => $resourceType,
@@ -957,7 +954,7 @@ App::shutdown()
])));
} elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) {
$cacheLog->setAttribute('accessedAt', $now);
$authorization->skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog));
Authorization::skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog));
// Overwrite the file every APP_CACHE_UPDATE seconds to update the file modified time that is used in the TTL checks in cache->load()
$cache->save($key, $data['payload']);
}
@@ -969,7 +966,7 @@ App::shutdown()
}
if ($project->getId() !== 'console') {
if (!User::isPrivileged($authorization->getRoles())) {
if (!User::isPrivileged(Authorization::getRoles())) {
$fileSize = 0;
$file = $request->getFiles('file');
if (!empty($file)) {
+3 -4
View File
@@ -36,8 +36,7 @@ App::init()
->inject('request')
->inject('project')
->inject('geodb')
->inject('authorization')
->action(function (App $utopia, Request $request, Document $project, Reader $geodb, Authorization $authorization) {
->action(function (App $utopia, Request $request, Document $project, Reader $geodb) {
$denylist = System::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', '');
if (!empty($denylist && $project->getId() === 'console')) {
$countries = explode(',', $denylist);
@@ -50,8 +49,8 @@ App::init()
$route = $utopia->match($request);
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAppUser = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$isAppUser = User::isApp(Authorization::getRoles());
if ($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs
return;
+11 -17
View File
@@ -25,6 +25,7 @@ use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Logger\Log;
use Utopia\Logger\Log\User;
use Utopia\Pools\Group;
@@ -258,9 +259,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
createDatabase($app, 'getLogsDB', 'logs', $collections['logs'], $pools);
// create appwrite database, `dbForPlatform` is a direct access call.
createDatabase($app, 'dbForPlatform', 'appwrite', $collections['console'], $pools, function (Database $dbForPlatform) use ($collections, $app) {
$authorization = $app->getResource('authorization');
createDatabase($app, 'dbForPlatform', 'appwrite', $collections['console'], $pools, function (Database $dbForPlatform) use ($collections) {
if ($dbForPlatform->getCollection(Audit::COLLECTION)->isEmpty()) {
$audit = new Audit($dbForPlatform);
$audit->setup();
@@ -319,9 +318,9 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
$dbForPlatform->createCollection('bucket_' . $bucket->getSequence(), $attributes, $indexes);
}
if ($authorization->skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots')->isEmpty())) {
if (Authorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots')->isEmpty())) {
Console::info(" └── Creating screenshots bucket...");
$authorization->skip(fn () => $dbForPlatform->createDocument('buckets', new Document([
Authorization::skip(fn () => $dbForPlatform->createDocument('buckets', new Document([
'$id' => ID::custom('screenshots'),
'$collection' => ID::custom('buckets'),
'name' => 'Screenshots',
@@ -336,7 +335,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
'search' => 'buckets Screenshots',
])));
$bucket = $authorization->skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots'));
$bucket = Authorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots'));
Console::info(" └── Creating files collection for screenshots bucket...");
$files = $collections['buckets']['files'] ?? [];
@@ -364,7 +363,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
'orders' => $index['orders'],
]), $files['indexes']);
$authorization->skip(fn () => $dbForPlatform->createCollection('bucket_' . $bucket->getSequence(), $attributes, $indexes));
Authorization::skip(fn () => $dbForPlatform->createCollection('bucket_' . $bucket->getSequence(), $attributes, $indexes));
}
});
@@ -455,12 +454,8 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
App::setResource('pools', fn () => $pools);
try {
$authorization = $app->getResource('authorization');
$request->setAuthorization($authorization);
$response->setAuthorization($authorization);
$authorization->cleanRoles();
$authorization->addRole(Role::any()->toString());
Authorization::cleanRoles();
Authorization::setRole(Role::any()->toString());
$app->run($request, $response);
} catch (\Throwable $th) {
@@ -502,7 +497,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
$log->addExtra('file', $th->getFile());
$log->addExtra('line', $th->getLine());
$log->addExtra('trace', $th->getTraceAsString());
$log->addExtra('roles', isset($authorization) ? $authorization->getRoles() : []);
$log->addExtra('roles', Authorization::getRoles());
$sdk = $route->getLabel("sdk", false);
@@ -561,7 +556,7 @@ $http->on(Constant::EVENT_TASK, function () use ($register, $domains) {
/** @var Utopia\Database\Database $dbForPlatform */
$dbForPlatform = $app->getResource('dbForPlatform');
Timer::tick(DOMAIN_SYNC_TIMER * 1000, function () use ($dbForPlatform, $domains, &$lastSyncUpdate, $app) {
Timer::tick(DOMAIN_SYNC_TIMER * 1000, function () use ($dbForPlatform, $domains, &$lastSyncUpdate) {
try {
$time = DateTime::now();
$limit = 1000;
@@ -578,8 +573,7 @@ $http->on(Constant::EVENT_TASK, function () use ($register, $domains) {
}
$results = [];
try {
$authorization = $app->getResource('authorization');
$results = $authorization->skip(fn () => $dbForPlatform->find('rules', $queries));
$results = Authorization::skip(fn () => $dbForPlatform->find('rules', $queries));
} catch (Throwable $th) {
Console::error($th->getMessage());
}
+24 -23
View File
@@ -4,6 +4,7 @@ use Appwrite\OpenSSL\OpenSSL;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\System\System;
Database::addFilter(
@@ -69,11 +70,11 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
$attributes = $database->getAuthorization()->skip(fn () => $database->find('attributes', [
$attributes = $database->find('attributes', [
Query::equal('collectionInternalId', [$document->getSequence()]),
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
Query::limit($database->getLimitForAttributes()),
]));
]);
foreach ($attributes as $attribute) {
$attributeType = $attribute->getAttribute('type');
@@ -104,12 +105,12 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database->getAuthorization()->skip(fn () => $database
return $database
->find('indexes', [
Query::equal('collectionInternalId', [$document->getSequence()]),
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
Query::limit($database->getLimitForIndexes()),
]));
]);
}
);
@@ -119,11 +120,11 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database->getAuthorization()->skip(fn () => $database
return $database
->find('platforms', [
Query::equal('projectInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
]);
}
);
@@ -133,11 +134,11 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database->getAuthorization()->skip(fn () => $database
return $database
->find('keys', [
Query::equal('projectInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
]);
}
);
@@ -147,11 +148,11 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database->getAuthorization()->skip(fn () => $database
return $database
->find('devKeys', [
Query::equal('projectInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
]);
}
);
@@ -161,11 +162,11 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database->getAuthorization()->skip(fn () => $database
return $database
->find('webhooks', [
Query::equal('projectInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
]);
}
);
@@ -175,7 +176,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database->getAuthorization()->skip(fn () => $database->find('sessions', [
return Authorization::skip(fn () => $database->find('sessions', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
@@ -188,7 +189,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database->getAuthorization()->skip(fn () => $database
return Authorization::skip(fn () => $database
->find('tokens', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
@@ -202,7 +203,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database->getAuthorization()->skip(fn () => $database
return Authorization::skip(fn () => $database
->find('challenges', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
@@ -216,7 +217,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database->getAuthorization()->skip(fn () => $database
return Authorization::skip(fn () => $database
->find('authenticators', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
@@ -230,7 +231,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database->getAuthorization()->skip(fn () => $database
return Authorization::skip(fn () => $database
->find('memberships', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
@@ -250,14 +251,14 @@ Database::addFilter(
default => ['function', 'site']
};
return $database->getAuthorization()->skip(fn () => $database
return $database
->find('variables', [
Query::equal('resourceInternalId', [$document->getSequence()]),
Query::equal('resourceType', $resourceType),
Query::orderAsc('resourceType'),
Query::orderAsc(),
Query::limit(APP_LIMIT_SUBQUERY),
]));
]);
}
);
@@ -293,11 +294,11 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database->getAuthorization()->skip(fn () => $database
return $database
->find('variables', [
Query::equal('resourceType', ['project']),
Query::limit(APP_LIMIT_SUBQUERY)
]));
]);
}
);
@@ -330,7 +331,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database->getAuthorization()->skip(fn () => $database
return Authorization::skip(fn () => $database
->find('targets', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY)
@@ -344,7 +345,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
$targetIds = $database->getAuthorization()->skip(fn () => \array_map(
$targetIds = Authorization::skip(fn () => \array_map(
fn ($document) => $document->getAttribute('targetInternalId'),
$database->find('subscribers', [
Query::equal('topicInternalId', [$document->getSequence()]),
+49 -58
View File
@@ -224,7 +224,7 @@ App::setResource('allowedSchemes', function (Document $project) {
/**
* Rule associated with a request origin.
*/
App::setResource('rule', function (Request $request, Database $dbForPlatform, Document $project, Authorization $authorization) {
App::setResource('rule', function (Request $request, Database $dbForPlatform, Document $project) {
$domain = \parse_url($request->getOrigin(), PHP_URL_HOST);
if (empty($domain)) {
return new Document();
@@ -232,7 +232,7 @@ App::setResource('rule', function (Request $request, Database $dbForPlatform, Do
// TODO: (@Meldiron) Remove after 1.7.x migration
$isMd5 = System::getEnv('_APP_RULES_FORMAT') === 'md5';
$rule = $authorization->skip(function () use ($dbForPlatform, $domain, $isMd5) {
$rule = Authorization::skip(function () use ($dbForPlatform, $domain, $isMd5) {
if ($isMd5) {
return $dbForPlatform->getDocument('rules', md5($domain));
}
@@ -247,7 +247,7 @@ App::setResource('rule', function (Request $request, Database $dbForPlatform, Do
}
return $rule;
}, ['request', 'dbForPlatform', 'project', 'authorization']);
}, ['request', 'dbForPlatform', 'project']);
/**
* CORS service
@@ -275,6 +275,7 @@ App::setResource('cors', fn (array $allowedHostnames) => new Cors(
'X-Appwrite-ID',
'X-Appwrite-Timestamp',
'X-Appwrite-Session',
'X-Appwrite-Platform', // for `$platform` injection and SDK generator
// SDK generator
'X-SDK-Version',
'X-SDK-Name',
@@ -314,7 +315,7 @@ App::setResource('redirectValidator', function (Document $devKey, array $allowed
return new Redirect($allowedHostnames, $allowedSchemes);
}, ['devKey', 'allowedHostnames', 'allowedSchemes']);
App::setResource('user', function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Store $store, Token $proofForToken, $authorization) {
App::setResource('user', function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Store $store, Token $proofForToken) {
/**
* Handles user authentication and session validation.
*
@@ -334,7 +335,7 @@ App::setResource('user', function (string $mode, Document $project, Document $co
* overwriting the previous value.
*/
$authorization->setDefaultStatus(true);
Authorization::setDefaultStatus(true);
$store->setKey('a_session_' . $project->getId());
@@ -401,7 +402,7 @@ App::setResource('user', function (string $mode, Document $project, Document $co
}
// if (APP_MODE_ADMIN === $mode) {
// if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) {
// $authorization->setDefaultStatus(false); // Cancel security segmentation for admin users.
// Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
// } else {
// $user = new Document([]);
// }
@@ -433,9 +434,9 @@ App::setResource('user', function (string $mode, Document $project, Document $co
$dbForPlatform->setMetadata('user', $user->getId());
return $user;
}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform', 'store', 'proofForToken', 'authorization']);
}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform', 'store', 'proofForToken']);
App::setResource('project', function ($dbForPlatform, $request, $console, $authorization) {
App::setResource('project', function ($dbForPlatform, $request, $console) {
/** @var Appwrite\Utopia\Request $request */
/** @var Utopia\Database\Database $dbForPlatform */
/** @var Utopia\Database\Document $console */
@@ -446,10 +447,10 @@ App::setResource('project', function ($dbForPlatform, $request, $console, $autho
return $console;
}
$project = $authorization->skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
return $project;
}, ['dbForPlatform', 'request', 'console', 'authorization']);
}, ['dbForPlatform', 'request', 'console']);
App::setResource('session', function (User $user, Store $store, Token $proofForToken) {
if ($user->isEmpty()) {
@@ -472,6 +473,10 @@ App::setResource('session', function (User $user, Store $store, Token $proofForT
return;
}, ['user', 'store', 'proofForToken']);
App::setResource('console', function () {
return new Document(Config::getParam('console'));
}, []);
App::setResource('store', function (): Store {
return new Store();
});
@@ -502,15 +507,7 @@ App::setResource('proofForCode', function (): Code {
return $code;
});
App::setResource('console', function () {
return new Document(Config::getParam('console'));
}, []);
App::setResource('authorization', function () {
return new Authorization();
}, []);
App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project, Authorization $authorization) {
App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project) {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
@@ -526,7 +523,6 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform
$database = new Database($adapter, $cache);
$database
->setAuthorization($authorization)
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId())
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
@@ -548,15 +544,13 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform
}
return $database;
}, ['pools', 'dbForPlatform', 'cache', 'project', 'authorization']);
App::setResource('dbForPlatform', function (Group $pools, Cache $cache, Authorization $authorization) {
}, ['pools', 'dbForPlatform', 'cache', 'project']);
App::setResource('dbForPlatform', function (Group $pools, Cache $cache) {
$adapter = new DatabasePool($pools->get('console'));
$database = new Database($adapter, $cache);
$database
->setAuthorization($authorization)
->setNamespace('_console')
->setMetadata('host', \gethostname())
->setMetadata('project', 'console')
@@ -566,12 +560,12 @@ App::setResource('dbForPlatform', function (Group $pools, Cache $cache, Authoriz
$database->setDocumentType('users', User::class);
return $database;
}, ['pools', 'cache', 'authorization']);
}, ['pools', 'cache']);
App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache, Authorization $authorization) {
App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) {
$databases = [];
return function (Document $project) use ($pools, $dbForPlatform, $cache, $authorization, &$databases) {
return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases) {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
@@ -583,15 +577,13 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
}
$configure = (function (Database $database) use ($project, $dsn, $authorization) {
$configure = (function (Database $database) use ($project, $dsn) {
$database
->setAuthorization($authorization)
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId())
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES)
->setDocumentType('users', User::class)
;
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
$database->setDocumentType('users', User::class);
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
@@ -621,12 +613,12 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
return $database;
};
}, ['pools', 'dbForPlatform', 'cache', 'authorization']);
}, ['pools', 'dbForPlatform', 'cache']);
App::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
$database = null;
return function (?Document $project = null) use ($pools, $cache, $authorization, &$database) {
return function (?Document $project = null) use ($pools, $cache, &$database) {
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant((int) $project->getSequence());
return $database;
@@ -636,7 +628,6 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorizatio
$database = new Database($adapter, $cache);
$database
->setAuthorization($authorization)
->setSharedTables(true)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
@@ -649,7 +640,7 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorizatio
return $database;
};
}, ['pools', 'cache', 'authorization']);
}, ['pools', 'cache']);
App::setResource('telemetry', fn () => new NoTelemetry());
@@ -843,7 +834,7 @@ App::setResource('promiseAdapter', function ($register) {
return $register->get('promiseAdapter');
}, ['register']);
App::setResource('schema', function ($utopia, $dbForProject, $authorization) {
App::setResource('schema', function ($utopia, $dbForProject) {
$complexity = function (int $complexity, array $args) {
$queries = Query::parseQueries($args['queries'] ?? []);
@@ -853,8 +844,8 @@ App::setResource('schema', function ($utopia, $dbForProject, $authorization) {
return $complexity * $limit;
};
$attributes = function (int $limit, int $offset) use ($dbForProject, $authorization) {
$attrs = $authorization->skip(fn () => $dbForProject->find('attributes', [
$attributes = function (int $limit, int $offset) use ($dbForProject) {
$attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [
Query::limit($limit),
Query::offset($offset),
]));
@@ -928,7 +919,7 @@ App::setResource('schema', function ($utopia, $dbForProject, $authorization) {
$urls,
$params,
);
}, ['utopia', 'dbForProject', 'authorization']);
}, ['utopia', 'dbForProject']);
App::setResource('gitHub', function (Cache $cache) {
return new VcsGitHub($cache);
@@ -956,7 +947,7 @@ App::setResource('smsRates', function () {
return [];
});
App::setResource('devKey', function (Request $request, Document $project, array $servers, Database $dbForPlatform, Authorization $authorization) {
App::setResource('devKey', function (Request $request, Document $project, array $servers, Database $dbForPlatform) {
$devKey = $request->getHeader('x-appwrite-dev-key', $request->getParam('devKey', ''));
// Check if given key match project's development keys
@@ -975,7 +966,7 @@ App::setResource('devKey', function (Request $request, Document $project, array
$accessedAt = $key->getAttribute('accessedAt', 0);
if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
$key->setAttribute('accessedAt', DatabaseDateTime::now());
$authorization->skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key));
Authorization::skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key));
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
@@ -992,15 +983,15 @@ App::setResource('devKey', function (Request $request, Document $project, array
/** Update access time as well */
$key->setAttribute('accessedAt', DatabaseDateTime::now());
$key = $authorization->skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key));
$key = Authorization::skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key));
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
}
return $key;
}, ['request', 'project', 'servers', 'dbForPlatform', 'authorization']);
}, ['request', 'project', 'servers', 'dbForPlatform']);
App::setResource('team', function (Document $project, Database $dbForPlatform, App $utopia, Request $request, Authorization $authorization) {
App::setResource('team', function (Document $project, Database $dbForPlatform, App $utopia, Request $request) {
$teamInternalId = '';
if ($project->getId() !== 'console') {
$teamInternalId = $project->getAttribute('teamInternalId', '');
@@ -1010,7 +1001,7 @@ App::setResource('team', function (Document $project, Database $dbForPlatform, A
if (str_starts_with($path, '/v1/projects/:projectId')) {
$uri = $request->getURI();
$pid = explode('/', $uri)[3];
$p = $authorization->skip(fn () => $dbForPlatform->getDocument('projects', $pid));
$p = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $pid));
$teamInternalId = $p->getAttribute('teamInternalId', '');
} elseif ($path === '/v1/projects') {
$teamId = $request->getParam('teamId', '');
@@ -1019,7 +1010,7 @@ App::setResource('team', function (Document $project, Database $dbForPlatform, A
return new Document([]);
}
$team = $authorization->skip(fn () => $dbForPlatform->getDocument('teams', $teamId));
$team = Authorization::skip(fn () => $dbForPlatform->getDocument('teams', $teamId));
return $team;
}
}
@@ -1028,14 +1019,14 @@ App::setResource('team', function (Document $project, Database $dbForPlatform, A
return new Document([]);
}
$team = $authorization->skip(function () use ($dbForPlatform, $teamInternalId) {
$team = Authorization::skip(function () use ($dbForPlatform, $teamInternalId) {
return $dbForPlatform->findOne('teams', [
Query::equal('$sequence', [$teamInternalId]),
]);
});
return $team;
}, ['project', 'dbForPlatform', 'utopia', 'request', 'authorization']);
}, ['project', 'dbForPlatform', 'utopia', 'request']);
App::setResource(
'isResourceBlocked',
@@ -1073,7 +1064,7 @@ App::setResource('apiKey', function (Request $request, Document $project): ?Key
App::setResource('executor', fn () => new Executor());
App::setResource('resourceToken', function ($project, $dbForProject, $request, Authorization $authorization) {
App::setResource('resourceToken', function ($project, $dbForProject, $request) {
$tokenJWT = $request->getParam('token');
if (!empty($tokenJWT) && !$project->isEmpty()) { // JWT authentication
@@ -1091,7 +1082,7 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request, A
return new Document([]);
}
$token = $authorization->skip(fn () => $dbForProject->getDocument('resourceTokens', $tokenId));
$token = Authorization::skip(fn () => $dbForProject->getDocument('resourceTokens', $tokenId));
if ($token->isEmpty()) {
return new Document([]);
@@ -1109,7 +1100,7 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request, A
}
return match ($token->getAttribute('resourceType')) {
TOKENS_RESOURCE_TYPE_FILES => (function () use ($token, $dbForProject, $authorization) {
TOKENS_RESOURCE_TYPE_FILES => (function () use ($token, $dbForProject) {
$sequences = explode(':', $token->getAttribute('resourceInternalId'));
$ids = explode(':', $token->getAttribute('resourceId'));
@@ -1120,7 +1111,7 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request, A
$accessedAt = $token->getAttribute('accessedAt', 0);
if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_RESOURCE_TOKEN_ACCESS)) > $accessedAt) {
$token->setAttribute('accessedAt', DatabaseDateTime::now());
$authorization->skip(fn () => $dbForProject->updateDocument('resourceTokens', $token->getId(), $token));
Authorization::skip(fn () => $dbForProject->updateDocument('resourceTokens', $token->getId(), $token));
}
return new Document([
@@ -1135,8 +1126,8 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request, A
};
}
return new Document([]);
}, ['project', 'dbForProject', 'request', 'authorization']);
}, ['project', 'dbForProject', 'request']);
App::setResource('transactionState', function (Database $dbForProject, Authorization $authorization) {
return new TransactionState($dbForProject, $authorization);
}, ['dbForProject', 'authorization']);
App::setResource('transactionState', function (Database $dbForProject) {
return new TransactionState($dbForProject);
}, ['dbForProject']);
+11 -30
View File
@@ -32,6 +32,7 @@ use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\DSN\DSN;
use Utopia\Logger\Log;
use Utopia\Pools\Group;
@@ -308,7 +309,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
'value' => '{}'
]);
$statsDocument = $database->getAuthorization()->skip(fn () => $database->createDocument('realtime', $document));
$statsDocument = Authorization::skip(fn () => $database->createDocument('realtime', $document));
break;
} catch (Throwable) {
Console::warning("Collection not ready. Retrying connection ({$attempts})...");
@@ -338,7 +339,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
->setAttribute('timestamp', DateTime::now())
->setAttribute('value', json_encode($payload));
$database->getAuthorization()->skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument));
Authorization::skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument));
} catch (Throwable $th) {
$logError($th, "updateWorkerDocument");
}
@@ -369,7 +370,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
$payload = [];
$list = $database->getAuthorization()->skip(fn () => $database->find('realtime', [
$list = Authorization::skip(fn () => $database->find('realtime', [
Query::greaterThan('timestamp', DateTime::addSeconds(new \DateTime(), -15)),
]));
@@ -463,13 +464,13 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
if ($realtime->hasSubscriber($projectId, 'user:' . $userId)) {
$connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId]));
$consoleDatabase = getConsoleDB();
$project = $consoleDatabase->getAuthorization()->skip(fn () => $consoleDatabase->getDocument('projects', $projectId));
$project = Authorization::skip(fn () => $consoleDatabase->getDocument('projects', $projectId));
$database = getProjectDB($project);
/** @var Appwrite\Utopia\Database\Documents\User $user */
$user = $database->getDocument('users', $userId);
$roles = $user->getRoles($database->getAuthorization());
$roles = $user->getRoles();
$channels = $realtime->connections[$connection]['channels'];
$realtime->unsubscribe($connection);
@@ -525,7 +526,6 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
try {
/** @var Document $project */
$project = $app->getResource('project');
$authorization = $app->getResource('authorization');
/*
* Project Check
@@ -537,7 +537,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
if (
array_key_exists('realtime', $project->getAttribute('apis', []))
&& !$project->getAttribute('apis', [])['realtime']
&& !(User::isPrivileged($authorization->getRoles()) || User::isApp($authorization->getRoles()))
&& !(User::isPrivileged(Authorization::getRoles()) || User::isApp(Authorization::getRoles()))
) {
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
}
@@ -573,7 +573,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $originValidator->getDescription());
}
$roles = $user->getRoles($authorization);
$roles = $user->getRoles();
$channels = Realtime::convertChannels($request->getQuery('channels', []), $user->getId());
@@ -586,8 +586,6 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
$realtime->subscribe($project->getId(), $connection, $roles, $channels);
$realtime->connections[$connection]['authorization'] = $authorization;
$user = empty($user->getId()) ? null : $response->output($user, Response::MODEL_ACCOUNT);
$server->send([$connection], json_encode([
@@ -616,7 +614,6 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
$code = 500;
}
$message = $th->getMessage();
// sanitize 0 && 5xx errors
@@ -646,19 +643,12 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
$server->onMessage(function (int $connection, string $message) use ($server, $register, $realtime, $containerId) {
try {
$response = new Response(new SwooleResponse());
$projectId = $realtime->connections[$connection]['projectId'] ?? null;
// Get authorization from connection (stored during onOpen)
$authorization = $realtime->connections[$connection]['authorization'] ?? null;
$projectId = $realtime->connections[$connection]['projectId'];
$database = getConsoleDB();
$database->setAuthorization($authorization);
if ($projectId !== 'console') {
$project = $authorization->skip(fn () => $database->getDocument('projects', $projectId));
$project = Authorization::skip(fn () => $database->getDocument('projects', $projectId));
$database = getProjectDB($project);
$database->setAuthorization($authorization);
} else {
$project = null;
}
@@ -722,19 +712,10 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Session is not valid.');
}
$roles = $user->getRoles($database->getAuthorization());
$roles = $user->getRoles();
$channels = Realtime::convertChannels(array_flip($realtime->connections[$connection]['channels']), $user->getId());
// Preserve authorization before subscribe overwrites the connection array
$authorization = $realtime->connections[$connection]['authorization'] ?? null;
$realtime->subscribe($realtime->connections[$connection]['projectId'], $connection, $roles, $channels);
// Restore authorization after subscribe
if ($authorization !== null) {
$realtime->connections[$connection]['authorization'] = $authorization;
}
$user = $response->output($user, Response::MODEL_ACCOUNT);
$server->send([$connection], json_encode([
'type' => 'response',
+18 -35
View File
@@ -46,30 +46,19 @@ use Utopia\System\System;
use Utopia\Telemetry\Adapter as Telemetry;
use Utopia\Telemetry\Adapter\None as NoTelemetry;
Authorization::disable();
Runtime::enableCoroutine();
Server::setResource('register', fn () => $register);
Server::setResource('authorization', function () {
$authorization = new Authorization();
$authorization->disable();
return $authorization;
}, []);
Server::setResource('dbForPlatform', function (Cache $cache, Registry $register, Authorization $authorization) {
Server::setResource('dbForPlatform', function (Cache $cache, Registry $register) {
$pools = $register->get('pools');
$adapter = new DatabasePool($pools->get('console'));
$dbForPlatform = new Database($adapter, $cache);
$dbForPlatform
->setAuthorization($authorization)
->setNamespace('_console')
->setDocumentType('users', User::class)
;
$dbForPlatform->setNamespace('_console');
$dbForPlatform->setDocumentType('users', User::class);
return $dbForPlatform;
}, ['cache', 'register', 'authorization']);
}, ['cache', 'register']);
Server::setResource('project', function (Message $message, Database $dbForPlatform) {
$payload = $message->getPayload() ?? [];
@@ -82,7 +71,7 @@ Server::setResource('project', function (Message $message, Database $dbForPlatfo
return $dbForPlatform->getDocument('projects', $project->getId());
}, ['message', 'dbForPlatform']);
Server::setResource('dbForProject', function (Cache $cache, Registry $register, Message $message, Document $project, Database $dbForPlatform, Authorization $authorization) {
Server::setResource('dbForProject', function (Cache $cache, Registry $register, Message $message, Document $project, Database $dbForPlatform) {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
@@ -114,17 +103,15 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register,
->setNamespace('_' . $project->getSequence());
}
$database
->setAuthorization($authorization)
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
$database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
return $database;
}, ['cache', 'register', 'message', 'project', 'dbForPlatform', 'authorization']);
}, ['cache', 'register', 'message', 'project', 'dbForPlatform']);
Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache, Authorization $authorization) {
Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) {
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
return function (Document $project) use ($pools, $dbForPlatform, $cache, $authorization, &$databases): Database {
return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases): Database {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
@@ -138,7 +125,7 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf
if (isset($databases[$dsn->getHost()])) {
$database = $databases[$dsn->getHost()];
$database->setAuthorization($authorization);
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
if (\in_array($dsn->getHost(), $sharedTables)) {
@@ -175,17 +162,15 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf
->setNamespace('_' . $project->getSequence());
}
$database
->setAuthorization($authorization)
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
$database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
return $database;
};
}, ['pools', 'dbForPlatform', 'cache', 'authorization']);
}, ['pools', 'dbForPlatform', 'cache']);
Server::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
$database = null;
return function (?Document $project = null) use ($pools, $cache, $database, $authorization) {
return function (?Document $project = null) use ($pools, $cache, $database) {
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant((int)$project->getSequence());
return $database;
@@ -195,7 +180,6 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache, Authoriza
$database = new Database($adapter, $cache);
$database
->setAuthorization($authorization)
->setSharedTables(true)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER)
@@ -208,7 +192,7 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache, Authoriza
return $database;
};
}, ['pools', 'cache', 'authorization']);
}, ['pools', 'cache']);
Server::setResource('abuseRetention', function () {
return time() - (int) System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400); // 1 day
@@ -510,8 +494,7 @@ $worker
->inject('log')
->inject('pools')
->inject('project')
->inject('authorization')
->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project, Authorization $authorization) use ($worker, $queueName) {
->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($worker, $queueName) {
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
if ($logger) {
@@ -527,7 +510,7 @@ $worker
$log->addExtra('file', $error->getFile());
$log->addExtra('line', $error->getLine());
$log->addExtra('trace', $error->getTraceAsString());
$log->addExtra('roles', $authorization->getRoles());
$log->addExtra('roles', Authorization::getRoles());
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
php /usr/src/code/app/cli.php interval $@
+1 -1
View File
@@ -52,7 +52,7 @@
"utopia-php/cache": "0.13.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "1.*.*",
"utopia-php/database": "4.*",
"utopia-php/database": "3.*",
"utopia-php/detector": "0.2.*",
"utopia-php/domains": "0.9.*",
"utopia-php/emails": "0.6.*",
Generated
+58 -58
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "99b8126a83fa0cb257df7c34ff2fdef5",
"content-hash": "7c9cb03eb5267f1e7a3ffc037ae22b6a",
"packages": [
{
"name": "adhocore/jwt",
@@ -3552,21 +3552,21 @@
},
{
"name": "utopia-php/audit",
"version": "1.0.3",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/audit.git",
"reference": "15656acfddb9d6f03c395b73673fc66c793c10a5"
"reference": "8c17065c2473d4ca799f65585ca74eb53e1be211"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/15656acfddb9d6f03c395b73673fc66c793c10a5",
"reference": "15656acfddb9d6f03c395b73673fc66c793c10a5",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/8c17065c2473d4ca799f65585ca74eb53e1be211",
"reference": "8c17065c2473d4ca799f65585ca74eb53e1be211",
"shasum": ""
},
"require": {
"php": ">=8.0",
"utopia-php/database": "4.*"
"utopia-php/database": "*"
},
"require-dev": {
"laravel/pint": "1.*",
@@ -3593,9 +3593,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/audit/issues",
"source": "https://github.com/utopia-php/audit/tree/1.0.3"
"source": "https://github.com/utopia-php/audit/tree/1.0.2"
},
"time": "2025-11-04T11:27:42+00:00"
"time": "2025-10-20T07:14:26+00:00"
},
{
"name": "utopia-php/auth",
@@ -3654,16 +3654,16 @@
},
{
"name": "utopia-php/cache",
"version": "0.13.2",
"version": "0.13.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/cache.git",
"reference": "5768498c9f451482f0bf3eede4d6452ddcd4a0f6"
"reference": "97220cb3b3822b166ee016d1646e2ae2815dc540"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/cache/zipball/5768498c9f451482f0bf3eede4d6452ddcd4a0f6",
"reference": "5768498c9f451482f0bf3eede4d6452ddcd4a0f6",
"url": "https://api.github.com/repos/utopia-php/cache/zipball/97220cb3b3822b166ee016d1646e2ae2815dc540",
"reference": "97220cb3b3822b166ee016d1646e2ae2815dc540",
"shasum": ""
},
"require": {
@@ -3672,7 +3672,7 @@
"ext-redis": "*",
"php": ">=8.0",
"utopia-php/pools": "0.8.*",
"utopia-php/telemetry": "*"
"utopia-php/telemetry": "0.1.*"
},
"require-dev": {
"laravel/pint": "1.2.*",
@@ -3700,9 +3700,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/cache/issues",
"source": "https://github.com/utopia-php/cache/tree/0.13.2"
"source": "https://github.com/utopia-php/cache/tree/0.13.1"
},
"time": "2025-12-17T08:55:43+00:00"
"time": "2025-05-09T14:43:52+00:00"
},
{
"name": "utopia-php/cli",
@@ -3896,16 +3896,16 @@
},
{
"name": "utopia-php/database",
"version": "4.3.0",
"version": "3.6.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "fe7a1326ad623609e65587fe8c01a630a7075fee"
"reference": "af15066255a5fd7bd2926de37bcbf3d8500fc155"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/fe7a1326ad623609e65587fe8c01a630a7075fee",
"reference": "fe7a1326ad623609e65587fe8c01a630a7075fee",
"url": "https://api.github.com/repos/utopia-php/database/zipball/af15066255a5fd7bd2926de37bcbf3d8500fc155",
"reference": "af15066255a5fd7bd2926de37bcbf3d8500fc155",
"shasum": ""
},
"require": {
@@ -3948,9 +3948,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/4.3.0"
"source": "https://github.com/utopia-php/database/tree/3.6.0"
},
"time": "2025-11-14T03:43:10+00:00"
"time": "2025-12-08T05:23:04+00:00"
},
{
"name": "utopia-php/detector",
@@ -3999,23 +3999,23 @@
},
{
"name": "utopia-php/dns",
"version": "1.4.1",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/dns.git",
"reference": "5daf8b683dad877491c4df84c6be24850b2f363b"
"reference": "dce3453364a4524b7250db8d8eb74820b814409e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/dns/zipball/5daf8b683dad877491c4df84c6be24850b2f363b",
"reference": "5daf8b683dad877491c4df84c6be24850b2f363b",
"url": "https://api.github.com/repos/utopia-php/dns/zipball/dce3453364a4524b7250db8d8eb74820b814409e",
"reference": "dce3453364a4524b7250db8d8eb74820b814409e",
"shasum": ""
},
"require": {
"php": ">=8.3",
"utopia-php/console": "0.0.*",
"utopia-php/domains": "0.9.*",
"utopia-php/telemetry": "*",
"utopia-php/telemetry": "0.1.*",
"utopia-php/validators": "0.*"
},
"require-dev": {
@@ -4050,9 +4050,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/dns/issues",
"source": "https://github.com/utopia-php/dns/tree/1.4.1"
"source": "https://github.com/utopia-php/dns/tree/1.4.0"
},
"time": "2025-12-17T09:09:08+00:00"
"time": "2025-12-05T10:09:00+00:00"
},
{
"name": "utopia-php/domains",
@@ -4513,16 +4513,16 @@
},
{
"name": "utopia-php/migration",
"version": "1.3.5",
"version": "1.3.9",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "6f366f1d4ac2796e59a97d1ba28cedc355e7122e"
"reference": "c55ec67c74663190cda10fd79297422147be7e85"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/6f366f1d4ac2796e59a97d1ba28cedc355e7122e",
"reference": "6f366f1d4ac2796e59a97d1ba28cedc355e7122e",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/c55ec67c74663190cda10fd79297422147be7e85",
"reference": "c55ec67c74663190cda10fd79297422147be7e85",
"shasum": ""
},
"require": {
@@ -4531,7 +4531,7 @@
"ext-openssl": "*",
"php": ">=8.1",
"utopia-php/console": "0.0.*",
"utopia-php/database": "4.*",
"utopia-php/database": "3.*",
"utopia-php/dsn": "0.2.*",
"utopia-php/storage": "0.18.*"
},
@@ -4562,9 +4562,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/1.3.5"
"source": "https://github.com/utopia-php/migration/tree/1.3.9"
},
"time": "2025-11-25T11:18:29+00:00"
"time": "2025-12-08T08:45:09+00:00"
},
{
"name": "utopia-php/mongo",
@@ -4730,21 +4730,21 @@
},
{
"name": "utopia-php/pools",
"version": "0.8.3",
"version": "0.8.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/pools.git",
"reference": "ad7d6ba946376e81c603204285ce9a674b6502b8"
"reference": "05c67aba42eb68ac65489cc1e7fc5db83db2dd4d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/pools/zipball/ad7d6ba946376e81c603204285ce9a674b6502b8",
"reference": "ad7d6ba946376e81c603204285ce9a674b6502b8",
"url": "https://api.github.com/repos/utopia-php/pools/zipball/05c67aba42eb68ac65489cc1e7fc5db83db2dd4d",
"reference": "05c67aba42eb68ac65489cc1e7fc5db83db2dd4d",
"shasum": ""
},
"require": {
"php": ">=8.4",
"utopia-php/telemetry": "*"
"php": ">=8.3",
"utopia-php/telemetry": "0.1.*"
},
"require-dev": {
"laravel/pint": "1.*",
@@ -4776,9 +4776,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/pools/issues",
"source": "https://github.com/utopia-php/pools/tree/0.8.3"
"source": "https://github.com/utopia-php/pools/tree/0.8.2"
},
"time": "2025-12-17T09:35:18+00:00"
"time": "2025-04-17T02:04:54+00:00"
},
{
"name": "utopia-php/preloader",
@@ -4835,16 +4835,16 @@
},
{
"name": "utopia-php/queue",
"version": "0.11.2",
"version": "0.11.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/queue.git",
"reference": "a854f7c4abc18e0eca55fc5608cd7088d71eb19f"
"reference": "498bbbef418b1db71b51e1bb62f5d1d752ddd8d6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/queue/zipball/a854f7c4abc18e0eca55fc5608cd7088d71eb19f",
"reference": "a854f7c4abc18e0eca55fc5608cd7088d71eb19f",
"url": "https://api.github.com/repos/utopia-php/queue/zipball/498bbbef418b1db71b51e1bb62f5d1d752ddd8d6",
"reference": "498bbbef418b1db71b51e1bb62f5d1d752ddd8d6",
"shasum": ""
},
"require": {
@@ -4854,7 +4854,7 @@
"utopia-php/fetch": "0.4.*",
"utopia-php/framework": "0.33.*",
"utopia-php/pools": "0.8.*",
"utopia-php/telemetry": "*"
"utopia-php/telemetry": "0.1.*"
},
"require-dev": {
"ext-redis": "*",
@@ -4895,9 +4895,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/queue/issues",
"source": "https://github.com/utopia-php/queue/tree/0.11.2"
"source": "https://github.com/utopia-php/queue/tree/0.11.1"
},
"time": "2025-12-17T09:32:35+00:00"
"time": "2025-05-30T11:50:34+00:00"
},
{
"name": "utopia-php/registry",
@@ -4953,16 +4953,16 @@
},
{
"name": "utopia-php/storage",
"version": "0.18.18",
"version": "0.18.16",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/storage.git",
"reference": "acaea524f315f87b8811a2c34450fe2b502f49d8"
"reference": "0c7b8ad68de8e1eb23ccc8af9f27a30eb832930f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/acaea524f315f87b8811a2c34450fe2b502f49d8",
"reference": "acaea524f315f87b8811a2c34450fe2b502f49d8",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/0c7b8ad68de8e1eb23ccc8af9f27a30eb832930f",
"reference": "0c7b8ad68de8e1eb23ccc8af9f27a30eb832930f",
"shasum": ""
},
"require": {
@@ -5005,9 +5005,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/storage/issues",
"source": "https://github.com/utopia-php/storage/tree/0.18.18"
"source": "https://github.com/utopia-php/storage/tree/0.18.16"
},
"time": "2025-12-17T07:33:45+00:00"
"time": "2025-12-03T02:15:45+00:00"
},
{
"name": "utopia-php/swoole",
@@ -8943,7 +8943,7 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
@@ -8967,5 +8967,5 @@
"platform-overrides": {
"php": "8.3"
},
"plugin-api-version": "2.9.0"
"plugin-api-version": "2.6.0"
}
+42
View File
@@ -555,6 +555,7 @@ services:
- _APP_DOMAIN_TARGET_CAA
- _APP_DNS
- _APP_DOMAIN_FUNCTIONS
- _APP_DOMAIN_SITES
- _APP_EMAIL_CERTIFICATES
- _APP_REDIS_HOST
- _APP_REDIS_PORT
@@ -753,6 +754,7 @@ services:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- mariadb
- redis
environment:
- _APP_ENV
@@ -785,6 +787,43 @@ services:
- _APP_MAINTENANCE_START_TIME
- _APP_DATABASE_SHARED_TABLES
appwrite-task-interval:
entrypoint: interval
<<: *x-logging
container_name: appwrite-task-interval
image: appwrite-dev
networks:
- appwrite
volumes:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- mariadb
- redis
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_DOMAIN
- _APP_DOMAIN_TARGET_CNAME
- _APP_DOMAIN_TARGET_AAAA
- _APP_DOMAIN_TARGET_A
- _APP_DOMAIN_TARGET_CAA
- _APP_DNS
- _APP_DOMAIN_FUNCTIONS
- _APP_DOMAIN_SITES
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_DATABASE_SHARED_TABLES
- _APP_INTERVAL_DOMAIN_VERIFICATION
appwrite-task-stats-resources:
container_name: appwrite-task-stats-resources
entrypoint: stats-resources
@@ -1131,6 +1170,9 @@ services:
- "traefik.http.routers.appwrite_maildev_https.rule=Host(`mail.localhost`)"
- "traefik.http.routers.appwrite_maildev_https.service=appwrite_maildev"
- "traefik.http.routers.appwrite_maildev_https.tls=true"
environment:
- MAILDEV_INCOMING_USER=${_APP_SMTP_USERNAME}
- MAILDEV_INCOMING_PASS=${_APP_SMTP_PASSWORD}
request-catcher-webhook: # used mainly for dev tests (mock HTTP webhook)
image: appwrite/requestcatcher:1.0.0
+4
View File
@@ -8,6 +8,10 @@ interface Adapter
{
public function issueCertificate(string $certName, string $domain, ?string $domainType): ?string;
public function isInstantGeneration(string $domain, ?string $domainType): bool;
public function getCertificateStatus(string $domain, ?string $domainType): string;
public function isRenewRequired(string $domain, ?string $domainType, Log $log): bool;
public function deleteCertificate(string $domain): void;
@@ -0,0 +1,10 @@
<?php
namespace Appwrite\Certificates\Exception;
use Exception;
// Exception thrown during certificate status retrieval
class CertificateStatus extends Exception
{
}
+11
View File
@@ -2,6 +2,7 @@
namespace Appwrite\Certificates;
use Appwrite\Certificates\Exception\CertificateStatus as CertificateStatusException;
use Exception;
use Utopia\App;
use Utopia\CLI\Console;
@@ -84,6 +85,16 @@ class LetsEncrypt implements Adapter
return DateTime::addSeconds($dt, -60 * 60 * 24 * 30);
}
public function isInstantGeneration(string $domain, ?string $domainType): bool
{
return true;
}
public function getCertificateStatus(string $domain, ?string $domainType): string
{
throw new CertificateStatusException('Certificate status retrieval is not supported for LetsEncrypt.');
}
public function isRenewRequired(string $domain, ?string $domainType, Log $log): bool
{
$certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem';
+4 -6
View File
@@ -20,12 +20,10 @@ use Utopia\Database\Validator\Authorization;
class TransactionState
{
private Database $dbForProject;
private Authorization $authorization;
/** @var Authorization $authorization */
public function __construct(Database $dbForProject, Authorization $authorization)
public function __construct(Database $dbForProject)
{
$this->dbForProject = $dbForProject;
$this->authorization = $authorization;
}
@@ -344,12 +342,12 @@ class TransactionState
*/
private function getTransactionState(string $transactionId): array
{
$transaction = $this->authorization->skip(fn () => $this->dbForProject->getDocument('transactions', $transactionId));
$transaction = Authorization::skip(fn () => $this->dbForProject->getDocument('transactions', $transactionId));
if ($transaction->isEmpty() || $transaction->getAttribute('status') !== 'pending') {
return [];
}
$operations = $this->authorization->skip(fn () => $this->dbForProject->find('transactionLogs', [
$operations = Authorization::skip(fn () => $this->dbForProject->find('transactionLogs', [
Query::equal('transactionInternalId', [$transaction->getSequence()]),
Query::orderAsc(),
Query::limit(PHP_INT_MAX)
+3 -2
View File
@@ -3,6 +3,7 @@
namespace Appwrite\Event;
use Utopia\Queue\Publisher;
use Utopia\System\System;
class Audit extends Event
{
@@ -19,8 +20,8 @@ class Audit extends Event
parent::__construct($publisher);
$this
->setQueue(Event::AUDITS_QUEUE_NAME)
->setClass(Event::AUDITS_CLASS_NAME);
->setQueue(System::getEnv('_APP_AUDITS_QUEUE_NAME', Event::AUDITS_QUEUE_NAME))
->setClass(System::getEnv('_APP_AUDITS_CLASS_NAME', Event::AUDITS_CLASS_NAME));
}
/**
+3 -2
View File
@@ -5,6 +5,7 @@ namespace Appwrite\Event;
use Utopia\Config\Config;
use Utopia\Database\Document;
use Utopia\Queue\Publisher;
use Utopia\System\System;
class Build extends Event
{
@@ -18,8 +19,8 @@ class Build extends Event
parent::__construct($publisher);
$this
->setQueue(Event::BUILDS_QUEUE_NAME)
->setClass(Event::BUILDS_CLASS_NAME);
->setQueue(System::getEnv('_APP_BUILDS_QUEUE_NAME', Event::BUILDS_QUEUE_NAME))
->setClass(System::getEnv('_APP_BUILDS_CLASS_NAME', Event::BUILDS_CLASS_NAME));
}
/**
+30 -3
View File
@@ -4,10 +4,14 @@ namespace Appwrite\Event;
use Utopia\Database\Document;
use Utopia\Queue\Publisher;
use Utopia\System\System;
class Certificate extends Event
{
public const string ACTION_DOMAIN_VERIFICATION = 'verification';
public const string ACTION_GENERATION = 'generation';
protected bool $skipRenewCheck = false;
protected string $action = self::ACTION_GENERATION;
protected ?Document $domain = null;
protected ?string $validationDomain = null;
@@ -16,8 +20,8 @@ class Certificate extends Event
parent::__construct($publisher);
$this
->setQueue(Event::CERTIFICATES_QUEUE_NAME)
->setClass(Event::CERTIFICATES_CLASS_NAME);
->setQueue(System::getEnv('_APP_CERTIFICATES_QUEUE_NAME', Event::CERTIFICATES_QUEUE_NAME))
->setClass(System::getEnv('_APP_CERTIFICATES_CLASS_NAME', Event::CERTIFICATES_CLASS_NAME));
}
/**
@@ -90,6 +94,28 @@ class Certificate extends Event
return $this->skipRenewCheck;
}
/**
* Set action for this certificate event.
*
* @param string $action
* @return self
*/
public function setAction(string $action): self
{
$this->action = $action;
return $this;
}
/**
* Get action for this certificate event.
*
* @return string
*/
public function getAction(): string
{
return $this->action;
}
/**
* Prepare the payload for the event
@@ -102,7 +128,8 @@ class Certificate extends Event
'project' => $this->project,
'domain' => $this->domain,
'skipRenewCheck' => $this->skipRenewCheck,
'validationDomain' => $this->validationDomain
'validationDomain' => $this->validationDomain,
'action' => $this->action
];
}
}
+2 -1
View File
@@ -5,6 +5,7 @@ namespace Appwrite\Event;
use Utopia\Database\Document;
use Utopia\DSN\DSN;
use Utopia\Queue\Publisher;
use Utopia\System\System;
class Database extends Event
{
@@ -24,7 +25,7 @@ class Database extends Event
{
parent::__construct($publisher);
$this->setClass(Event::DATABASE_CLASS_NAME);
$this->setClass(System::getEnv('_APP_DATABASE_CLASS_NAME', Event::DATABASE_CLASS_NAME));
}
/**
+3 -2
View File
@@ -4,6 +4,7 @@ namespace Appwrite\Event;
use Utopia\Database\Document;
use Utopia\Queue\Publisher;
use Utopia\System\System;
class Delete extends Event
{
@@ -20,8 +21,8 @@ class Delete extends Event
parent::__construct($publisher);
$this
->setQueue(Event::DELETE_QUEUE_NAME)
->setClass(Event::DELETE_CLASS_NAME);
->setQueue(System::getEnv('_APP_DELETE_QUEUE_NAME', Event::DELETE_QUEUE_NAME))
->setClass(System::getEnv('_APP_DELETE_CLASS_NAME', Event::DELETE_CLASS_NAME));
}
/**
+3 -2
View File
@@ -5,6 +5,7 @@ namespace Appwrite\Event;
use Utopia\Config\Config;
use Utopia\Database\Document;
use Utopia\Queue\Publisher;
use Utopia\System\System;
class Func extends Event
{
@@ -25,8 +26,8 @@ class Func extends Event
parent::__construct($publisher);
$this
->setQueue(Event::FUNCTIONS_QUEUE_NAME)
->setClass(Event::FUNCTIONS_CLASS_NAME);
->setQueue(System::getEnv('_APP_FUNCTIONS_QUEUE_NAME', Event::FUNCTIONS_QUEUE_NAME))
->setClass(System::getEnv('_APP_FUNCTIONS_CLASS_NAME', Event::FUNCTIONS_CLASS_NAME));
}
/**
+3 -2
View File
@@ -4,6 +4,7 @@ namespace Appwrite\Event;
use Utopia\Config\Config;
use Utopia\Queue\Publisher;
use Utopia\System\System;
class Mail extends Event
{
@@ -24,8 +25,8 @@ class Mail extends Event
parent::__construct($publisher);
$this
->setQueue(Event::MAILS_QUEUE_NAME)
->setClass(Event::MAILS_CLASS_NAME);
->setQueue(System::getEnv('_APP_MAILS_QUEUE_NAME', Event::MAILS_QUEUE_NAME))
->setClass(System::getEnv('_APP_MAILS_CLASS_NAME', Event::MAILS_CLASS_NAME));
}
/**
+3 -2
View File
@@ -4,6 +4,7 @@ namespace Appwrite\Event;
use Utopia\Database\Document;
use Utopia\Queue\Publisher;
use Utopia\System\System;
class Messaging extends Event
{
@@ -19,8 +20,8 @@ class Messaging extends Event
parent::__construct($publisher);
$this
->setQueue(Event::MESSAGING_QUEUE_NAME)
->setClass(Event::MESSAGING_CLASS_NAME);
->setQueue(System::getEnv('_APP_MESSAGING_QUEUE_NAME', Event::MESSAGING_QUEUE_NAME))
->setClass(System::getEnv('_APP_MESSAGING_CLASS_NAME', Event::MESSAGING_CLASS_NAME));
}
/**
+3 -2
View File
@@ -5,6 +5,7 @@ namespace Appwrite\Event;
use Utopia\Config\Config;
use Utopia\Database\Document;
use Utopia\Queue\Publisher;
use Utopia\System\System;
class Migration extends Event
{
@@ -16,8 +17,8 @@ class Migration extends Event
parent::__construct($publisher);
$this
->setQueue(Event::MIGRATIONS_QUEUE_NAME)
->setClass(Event::MIGRATIONS_CLASS_NAME);
->setQueue(System::getEnv('_APP_MIGRATIONS_QUEUE_NAME', Event::MIGRATIONS_QUEUE_NAME))
->setClass(System::getEnv('_APP_MIGRATIONS_CLASS_NAME', Event::MIGRATIONS_CLASS_NAME));
}
/**
+3 -2
View File
@@ -3,6 +3,7 @@
namespace Appwrite\Event;
use Utopia\Queue\Publisher;
use Utopia\System\System;
class StatsResources extends Event
{
@@ -13,8 +14,8 @@ class StatsResources extends Event
parent::__construct($publisher);
$this
->setQueue(Event::STATS_RESOURCES_QUEUE_NAME)
->setClass(Event::STATS_RESOURCES_CLASS_NAME);
->setQueue(System::getEnv('_APP_STATS_RESOURCES_QUEUE_NAME', Event::STATS_RESOURCES_QUEUE_NAME))
->setClass(System::getEnv('_APP_STATS_RESOURCES_CLASS_NAME', Event::STATS_RESOURCES_CLASS_NAME));
}
/**
+3 -2
View File
@@ -4,6 +4,7 @@ namespace Appwrite\Event;
use Utopia\Database\Document;
use Utopia\Queue\Publisher;
use Utopia\System\System;
class StatsUsage extends Event
{
@@ -18,8 +19,8 @@ class StatsUsage extends Event
parent::__construct($publisher);
$this
->setQueue(Event::STATS_USAGE_QUEUE_NAME)
->setClass(Event::STATS_USAGE_CLASS_NAME);
->setQueue(System::getEnv('_APP_STATS_USAGE_QUEUE_NAME', Event::STATS_USAGE_QUEUE_NAME))
->setClass(System::getEnv('_APP_STATS_USAGE_CLASS_NAME', Event::STATS_USAGE_CLASS_NAME));
}
/**
+3 -2
View File
@@ -3,6 +3,7 @@
namespace Appwrite\Event;
use Utopia\Queue\Publisher;
use Utopia\System\System;
class Webhook extends Event
{
@@ -11,8 +12,8 @@ class Webhook extends Event
parent::__construct($publisher);
$this
->setQueue(Event::WEBHOOK_QUEUE_NAME)
->setClass(Event::WEBHOOK_CLASS_NAME);
->setQueue(System::getEnv('_APP_WEBHOOK_QUEUE_NAME', Event::WEBHOOK_QUEUE_NAME))
->setClass(System::getEnv('_APP_WEBHOOK_CLASS_NAME', Event::WEBHOOK_CLASS_NAME));
}
/**
+2 -4
View File
@@ -99,6 +99,8 @@ abstract class Migration
public function __construct()
{
Authorization::disable();
Authorization::setDefaultStatus(false);
$this->collections = Config::getParam('collections', []);
@@ -126,7 +128,6 @@ abstract class Migration
Document $project,
Database $dbForProject,
Database $dbForPlatform,
Authorization $authorization,
?callable $getProjectDB = null
): self {
$this->project = $project;
@@ -134,9 +135,6 @@ abstract class Migration
$this->dbForPlatform = $dbForPlatform;
$this->getProjectDB = $getProjectDB;
$authorization->disable();
$authorization->setDefaultStatus(false);
return $this;
}
+11 -4
View File
@@ -22,9 +22,9 @@ class Action extends UtopiaAction
protected mixed $logError;
protected array $filters = [
'subQueryKeys', 'subQueryWebhooks', 'subQueryPlatforms', 'subQueryProjectVariables', 'subQueryBlocks', 'subQueryDevKeys', // Project
'subQueryKeys', 'subQueryWebhooks', 'subQueryPlatforms', 'subQueryBlocks', 'subQueryDevKeys', // Project
'subQueryAuthenticators', 'subQuerySessions', 'subQueryTokens', 'subQueryChallenges', 'subQueryMemberships', 'subQueryTargets', 'subQueryTopicTargets',// Users
'subQueryVariables', // Sites
'subQueryVariables', 'subQueryProjectVariables' // Sites / Functions
];
/**
@@ -107,9 +107,11 @@ class Action extends UtopiaAction
}
}
public function disableSubqueries()
public function disableSubqueries(array $filters = []): void
{
$filters = $this->filters;
if (empty($filters)) {
$filters = $this->filters;
}
foreach ($filters as $filter) {
Database::addFilter(
@@ -189,6 +191,11 @@ class Action extends UtopiaAction
}
}
// found a wildcard, return!
if (\in_array('*', $attributes)) {
return;
}
$responseModel = $response->getModel($model);
foreach ($responseModel->getRules() as $ruleName => $rule) {
if (\str_starts_with($ruleName, '$')) {
+4 -37
View File
@@ -13,7 +13,6 @@ use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Swoole\Request;
use Utopia\System\System;
@@ -143,7 +142,7 @@ class Base extends Action
return $deployment;
}
public function redeployVcsSite(Request $request, Document $site, Document $project, Document $installation, Database $dbForProject, Database $dbForPlatform, Build $queueForBuilds, Document $template, GitHub $github, bool $activate, Authorization $authorization, string $referenceType = 'branch', string $reference = ''): Document
public function redeployVcsSite(Request $request, Document $site, Document $project, Document $installation, Database $dbForProject, Database $dbForPlatform, Build $queueForBuilds, Document $template, GitHub $github, bool $activate, string $referenceType = 'branch', string $reference = ''): Document
{
$deploymentId = ID::unique();
$providerInstallationId = $installation->getAttribute('providerInstallationId', '');
@@ -240,7 +239,7 @@ class Base extends Action
$isMd5 = System::getEnv('_APP_RULES_FORMAT') === 'md5';
$ruleId = $isMd5 ? md5($domain) : ID::unique();
$authorization->skip(
Authorization::skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -266,7 +265,7 @@ class Base extends Action
$domain = "commit-" . substr($commitDetails['commitHash'], 0, 16) . ".{$sitesDomain}";
$ruleId = md5($domain);
try {
$authorization->skip(
Authorization::skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -303,7 +302,7 @@ class Base extends Action
$domain = "branch-{$branchPrefix}-{$resourceProjectHash}.{$sitesDomain}";
$ruleId = md5($domain);
try {
$authorization->skip(
Authorization::skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -329,8 +328,6 @@ class Base extends Action
}
}
$this->updateEmptyManualRule($project, $site, $deployment, $dbForPlatform, $authorization);
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($site)
@@ -339,34 +336,4 @@ class Base extends Action
return $deployment;
}
/**
* Update empty manual rule for deployment.
* In case of first deployment, deployment ID will be empty in the rules, so we need to update it here.
*
* @param \Utopia\Database\Document $project
* @param \Utopia\Database\Document $resource
* @param \Utopia\Database\Document $deployment
* @param \Utopia\Database\Database $dbForPlatform
* @return void
*/
public static function updateEmptyManualRule(Document $project, Document $resource, Document $deployment, Database $dbForPlatform, Authorization $authorization)
{
$resourceType = $resource->getCollection() === 'sites' ? 'site' : 'function';
$queries = [
Query::equal('projectInternalId', [$project->getSequence()]),
Query::equal('deploymentResourceInternalId', [$resource->getSequence()]),
Query::equal('deploymentResourceType', [$resourceType]),
Query::equal('deploymentId', ['']),
Query::equal('type', ['deployment']),
Query::equal('trigger', ['manual']),
];
$dbForPlatform->forEach('rules', function (Document $rule) use ($deployment, $dbForPlatform, $authorization) {
$authorization->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
'deploymentId' => $deployment->getId(),
'deploymentInternalId' => $deployment->getSequence(),
])));
}, $queries);
}
}
@@ -60,7 +60,6 @@ class Get extends Action
->inject('response')
->inject('dbForPlatform')
->inject('platform')
->inject('authorization')
->callback($this->action(...));
}
@@ -69,8 +68,7 @@ class Get extends Action
string $type,
Response $response,
Database $dbForPlatform,
array $platform,
Authorization $authorization,
array $platform
) {
$domains = $platform['hostnames'] ?? [];
if ($type === 'rules') {
@@ -123,7 +121,7 @@ class Get extends Action
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Domain may not start with http:// or https://.');
}
$document = $authorization->skip(fn () => $dbForPlatform->findOne('rules', [
$document = Authorization::skip(fn () => $dbForPlatform->findOne('rules', [
Query::equal('domain', [$value]),
]));
@@ -292,7 +292,7 @@ abstract class Action extends UtopiaAction
};
}
protected function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): Document
protected function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): Document
{
$key = $attribute->getAttribute('key');
$type = $attribute->getAttribute('type', '');
@@ -310,7 +310,7 @@ abstract class Action extends UtopiaAction
throw new Exception($this->getSpatialTypeNotSupportedException(), params: [$type]);
}
$db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
@@ -371,7 +371,7 @@ abstract class Action extends UtopiaAction
\in_array($attribute->getAttribute('type'), Database::SPATIAL_TYPES) &&
$attribute->getAttribute('required')
) {
$hasData = !$authorization->skip(fn () => $dbForProject
$hasData = !Authorization::skip(fn () => $dbForProject
->findOne('database_' . $db->getSequence() . '_collection_' . $collection->getSequence()))
->isEmpty();
@@ -472,9 +472,9 @@ abstract class Action extends UtopiaAction
return $attribute;
}
protected function updateAttribute(string $databaseId, string $collectionId, string $key, Database $dbForProject, Event $queueForEvents, Authorization $authorization, string $type, int $size = null, string $filter = null, string|bool|int|float|array $default = null, bool $required = null, int|float|null $min = null, int|float|null $max = null, array $elements = null, array $options = [], string $newKey = null): Document
protected function updateAttribute(string $databaseId, string $collectionId, string $key, Database $dbForProject, Event $queueForEvents, string $type, int $size = null, string $filter = null, string|bool|int|float|array $default = null, bool $required = null, int|float|null $min = null, int|float|null $max = null, array $elements = null, array $options = [], string $newKey = null): Document
{
$db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
@@ -12,7 +12,6 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -70,11 +69,10 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
{
$attribute = $this->createAttribute($databaseId, $collectionId, new Document([
'key' => $key,
@@ -83,7 +81,7 @@ class Create extends Action
'required' => $required,
'default' => $default,
'array' => $array,
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
@@ -11,7 +11,6 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -69,11 +68,10 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -81,7 +79,6 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_BOOLEAN,
default: $default,
required: $required,
@@ -12,7 +12,6 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
@@ -71,11 +70,10 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
{
$attribute = $this->createAttribute(
$databaseId,
@@ -92,8 +90,7 @@ class Create extends Action
$response,
$dbForProject,
$queueForDatabase,
$queueForEvents,
$authorization
$queueForEvents
);
$response
@@ -11,7 +11,6 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
@@ -70,11 +69,10 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -82,7 +80,6 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_DATETIME,
default: $default,
required: $required,
@@ -67,13 +67,12 @@ class Delete extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
{
$db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
@@ -13,7 +13,6 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -71,11 +70,10 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
{
$attribute = $this->createAttribute(
$databaseId,
@@ -92,8 +90,7 @@ class Create extends Action
$response,
$dbForProject,
$queueForDatabase,
$queueForEvents,
$authorization
$queueForEvents
);
$response
@@ -12,7 +12,6 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -70,11 +69,10 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -82,7 +80,6 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_EMAIL,
default: $default,
@@ -13,7 +13,6 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -74,11 +73,10 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
{
if (!is_null($default) && !\in_array($default, $elements, true)) {
throw new Exception($this->getInvalidValueException(), 'Default value not found in elements');
@@ -100,8 +98,7 @@ class Create extends Action
$response,
$dbForProject,
$queueForDatabase,
$queueForEvents,
$authorization
$queueForEvents
);
$response
@@ -11,7 +11,6 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -72,11 +71,10 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -84,7 +82,6 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_ENUM,
default: $default,
@@ -13,7 +13,6 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -75,11 +74,10 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
{
$min ??= -PHP_FLOAT_MAX;
$max ??= PHP_FLOAT_MAX;
@@ -102,7 +100,7 @@ class Create extends Action
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_FLOAT_RANGE,
'formatOptions' => ['min' => $min, 'max' => $max],
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
$formatOptions = $attribute->getAttribute('formatOptions', []);
if (!empty($formatOptions)) {
@@ -11,7 +11,6 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -72,11 +71,10 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -84,7 +82,6 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_FLOAT,
default: $default,
required: $required,
@@ -68,13 +68,12 @@ class Get extends Action
->param('key', '', new Key(), 'Attribute Key.')
->inject('response')
->inject('dbForProject')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject): void
{
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
@@ -12,7 +12,6 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -71,11 +70,10 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
{
$attribute = $this->createAttribute(
$databaseId,
@@ -92,8 +90,7 @@ class Create extends Action
$response,
$dbForProject,
$queueForDatabase,
$queueForEvents,
$authorization
$queueForEvents
);
$response
@@ -11,7 +11,6 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -70,11 +69,10 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -82,7 +80,6 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_IP,
default: $default,
@@ -13,7 +13,6 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -75,11 +74,10 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
{
$min ??= \PHP_INT_MIN;
$max ??= \PHP_INT_MAX;
@@ -104,7 +102,7 @@ class Create extends Action
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_INT_RANGE,
'formatOptions' => ['min' => $min, 'max' => $max],
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
$formatOptions = $attribute->getAttribute('formatOptions', []);
if (!empty($formatOptions)) {
@@ -11,7 +11,6 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -72,11 +71,10 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -84,7 +82,6 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_INTEGER,
default: $default,
required: $required,
@@ -12,7 +12,6 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Spatial;
use Utopia\Database\Validator\UID;
@@ -70,18 +69,17 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
{
$attribute = $this->createAttribute($databaseId, $collectionId, new Document([
'key' => $key,
'type' => Database::VAR_LINESTRING,
'required' => $required,
'default' => $default
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
@@ -11,7 +11,6 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Spatial;
use Utopia\Database\Validator\UID;
@@ -70,11 +69,10 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -82,7 +80,6 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_LINESTRING,
default: $default,
required: $required,
@@ -12,7 +12,6 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Spatial;
use Utopia\Database\Validator\UID;
@@ -70,18 +69,17 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
{
$attribute = $this->createAttribute($databaseId, $collectionId, new Document([
'key' => $key,
'type' => Database::VAR_POINT,
'required' => $required,
'default' => $default,
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
@@ -11,7 +11,6 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Spatial;
use Utopia\Database\Validator\UID;
@@ -70,11 +69,10 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -82,7 +80,6 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_POINT,
default: $default,
required: $required,
@@ -12,7 +12,6 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Spatial;
use Utopia\Database\Validator\UID;
@@ -70,18 +69,17 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
{
$attribute = $this->createAttribute($databaseId, $collectionId, new Document([
'key' => $key,
'type' => Database::VAR_POLYGON,
'required' => $required,
'default' => $default,
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
@@ -11,7 +11,6 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Spatial;
use Utopia\Database\Validator\UID;
@@ -70,11 +69,10 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -82,7 +80,6 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_POLYGON,
default: $default,
required: $required,
@@ -83,17 +83,16 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $relatedCollectionId, string $type, bool $twoWay, ?string $key, ?string $twoWayKey, string $onDelete, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $relatedCollectionId, string $type, bool $twoWay, ?string $key, ?string $twoWayKey, string $onDelete, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
{
$key ??= $relatedCollectionId;
$twoWayKeyWasProvided = $twoWayKey !== null;
$twoWayKey ??= $collectionId;
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
@@ -155,7 +154,7 @@ class Create extends Action
'twoWayKey' => $twoWayKey,
'onDelete' => $onDelete,
]
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
foreach ($attribute->getAttribute('options', []) as $k => $option) {
$attribute->setAttribute($k, $option);
@@ -11,7 +11,6 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -72,7 +71,6 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
@@ -84,8 +82,7 @@ class Update extends Action
?string $newKey,
UtopiaResponse $response,
Database $dbForProject,
Event $queueForEvents,
Authorization $authorization
Event $queueForEvents
): void {
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -93,7 +90,6 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_RELATIONSHIP,
required: false,
options: [
@@ -14,7 +14,6 @@ use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\App;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -78,7 +77,6 @@ class Create extends Action
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('plan')
->inject('authorization')
->callback($this->action(...));
}
@@ -95,8 +93,7 @@ class Create extends Action
Database $dbForProject,
EventDatabase $queueForDatabase,
Event $queueForEvents,
array $plan,
Authorization $authorization
array $plan
): void {
if (!App::isDevelopment() && $encrypt && !empty($plan) && !($plan['databasesAllowEncrypt'] ?? false)) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Encrypted string ' . $this->getSDKGroup() . ' are not available on your plan. Please upgrade to create encrypted string ' . $this->getSDKGroup() . '.');
@@ -135,8 +132,7 @@ class Create extends Action
$response,
$dbForProject,
$queueForDatabase,
$queueForEvents,
$authorization
$queueForEvents
);
$attribute->setAttribute('encrypt', $encrypt);
@@ -11,7 +11,6 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -73,7 +72,6 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
@@ -87,8 +85,7 @@ class Update extends Action
?string $newKey,
UtopiaResponse $response,
Database $dbForProject,
Event $queueForEvents,
Authorization $authorization
Event $queueForEvents
): void {
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -96,7 +93,6 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_STRING,
size: $size,
default: $default,
@@ -12,7 +12,6 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -71,7 +70,6 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
@@ -85,8 +83,7 @@ class Create extends Action
UtopiaResponse $response,
Database $dbForProject,
EventDatabase $queueForDatabase,
Event $queueForEvents,
Authorization $authorization
Event $queueForEvents
): void {
$attribute = $this->createAttribute($databaseId, $collectionId, new Document([
'key' => $key,
@@ -96,7 +93,7 @@ class Create extends Action
'default' => $default,
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_URL,
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
@@ -11,7 +11,6 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -70,7 +69,6 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
@@ -83,8 +81,7 @@ class Update extends Action
?string $newKey,
UtopiaResponse $response,
Database $dbForProject,
Event $queueForEvents,
Authorization $authorization
Event $queueForEvents
): void {
$attribute = $this->updateAttribute(
$databaseId,
@@ -92,7 +89,6 @@ class Update extends Action
$key,
$dbForProject,
$queueForEvents,
$authorization,
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_URL,
default: $default,
@@ -64,13 +64,12 @@ class XList extends Action
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
->inject('response')
->inject('dbForProject')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, array $queries, bool $includeTotal, UtopiaResponse $response, Database $dbForProject, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, array $queries, bool $includeTotal, UtopiaResponse $response, Database $dbForProject): void
{
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
@@ -85,13 +85,12 @@ class Create extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, array $attributes, array $indexes, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, array $attributes, array $indexes, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
{
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
@@ -64,13 +64,12 @@ class Delete extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
{
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
@@ -258,9 +258,9 @@ abstract class Action extends DatabasesAction
Document $collection,
Document $document,
Database $dbForProject,
/* options */
array &$collectionsCache,
Authorization $authorization,
?int &$operations = null,
): bool {
@@ -297,7 +297,7 @@ abstract class Action extends DatabasesAction
$relatedCollectionId = $relationship->getAttribute('relatedCollection');
if (!isset($collectionsCache[$relatedCollectionId])) {
$relatedCollectionDoc = $authorization->skip(
$relatedCollectionDoc = Authorization::skip(
fn () => $dbForProject->getDocument(
'database_' . $database->getSequence(),
$relatedCollectionId
@@ -323,8 +323,7 @@ abstract class Action extends DatabasesAction
document: $relation,
dbForProject: $dbForProject,
collectionsCache: $collectionsCache,
operations: $operations,
authorization: $authorization
operations: $operations
);
}
}
@@ -383,9 +382,11 @@ abstract class Action extends DatabasesAction
->from($queueForEvents)
->trigger();
$queueForWebhooks
->from($queueForEvents)
->trigger();
if (!empty($queueForEvents->getProject()?->getAttribute('webhooks', []))) {
$queueForWebhooks
->from($queueForEvents)
->trigger();
}
}
$queueForEvents->reset();
@@ -85,21 +85,20 @@ class Decrement extends Action
->inject('queueForEvents')
->inject('queueForStatsUsage')
->inject('plan')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): void
{
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
$collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty()) {
throw new Exception($this->getParentNotFoundException(), params: [$collectionId]);
}
@@ -107,7 +106,7 @@ class Decrement extends Action
// Handle transaction staging
if ($transactionId !== null) {
$transaction = ($isAPIKey || $isPrivilegedUser)
? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
: $dbForProject->getDocument('transactions', $transactionId);
if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or nonpending transaction');
@@ -85,21 +85,20 @@ class Increment extends Action
->inject('queueForEvents')
->inject('queueForStatsUsage')
->inject('plan')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): void
{
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
$collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty()) {
throw new Exception($this->getParentNotFoundException(), params: [$collectionId]);
}
@@ -107,7 +106,7 @@ class Increment extends Action
// Handle transaction staging
if ($transactionId !== null) {
$transaction = ($isAPIKey || $isPrivilegedUser)
? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
: $dbForProject->getDocument('transactions', $transactionId);
if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or nonpending transaction');
@@ -24,7 +24,6 @@ use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Authorization\Input;
use Utopia\Database\Validator\Permissions;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -133,10 +132,9 @@ class Create extends Action
->inject('queueForFunctions')
->inject('queueForWebhooks')
->inject('plan')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan, Authorization $authorization): void
public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void
{
$data = \is_string($data)
? \json_decode($data, true)
@@ -180,19 +178,19 @@ class Create extends Action
$documents = [$data];
}
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
if ($isBulk && !$isAPIKey && !$isPrivilegedUser) {
throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE);
}
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
$collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception($this->getParentNotFoundException(), params: [$collectionId]);
}
@@ -206,7 +204,7 @@ class Create extends Action
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk create is not supported for ' . $this->getSDKNamespace() .' with relationship ' . $this->getStructureContext());
}
$setPermissions = function (Document $document, ?array $permissions) use ($user, $isAPIKey, $isPrivilegedUser, $isBulk, $dbForProject, $authorization) {
$setPermissions = function (Document $document, ?array $permissions) use ($user, $isAPIKey, $isPrivilegedUser, $isBulk) {
$allowedPermissions = [
Database::PERMISSION_READ,
Database::PERMISSION_UPDATE,
@@ -229,7 +227,7 @@ class Create extends Action
// Add permissions for current the user if none were provided.
if (\is_null($permissions)) {
$permissions = [];
if (!empty($user->getId())) {
if (!empty($user->getId()) && !$isPrivilegedUser) {
foreach ($allowedPermissions as $permission) {
$permissions[] = (new Permission($permission, 'user', $user->getId()))->toString();
}
@@ -249,8 +247,8 @@ class Create extends Action
$permission->getIdentifier(),
$permission->getDimension()
))->toString();
if (!$authorization->hasRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $authorization->getRoles()) . ')');
if (!Authorization::isRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', Authorization::getRoles()) . ')');
}
}
}
@@ -261,25 +259,21 @@ class Create extends Action
$operations = 0;
$checkPermissions = function (Document $collection, Document $document, string $permission) use ($isAPIKey, $isPrivilegedUser, &$checkPermissions, $dbForProject, $database, &$operations, $authorization) {
$checkPermissions = function (Document $collection, Document $document, string $permission) use ($isAPIKey, $isPrivilegedUser, &$checkPermissions, $dbForProject, $database, &$operations) {
$operations++;
$documentSecurity = $collection->getAttribute('documentSecurity', false);
$validator = new Authorization($permission);
$validCollection = $authorization->isValid(
new Input($permission, $collection->getPermissionsByType($permission))
);
if (($permission === Database::PERMISSION_UPDATE && !$documentSecurity) || !$validCollection) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
$valid = $validator->isValid($collection->getPermissionsByType($permission));
if (($permission === Database::PERMISSION_UPDATE && !$documentSecurity) || !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($permission === Database::PERMISSION_UPDATE) {
$validDocument = $authorization->isValid(
new Input($permission, $document->getUpdate())
);
$valid = $validCollection || $validDocument;
$valid = $valid || $validator->isValid($document->getUpdate());
if ($documentSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
throw new Exception(Exception::USER_UNAUTHORIZED);
}
}
@@ -304,7 +298,7 @@ class Create extends Action
}
$relatedCollectionId = $relationship->getAttribute('relatedCollection');
$relatedCollection = $authorization->skip(
$relatedCollection = Authorization::skip(
fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId)
);
@@ -320,7 +314,7 @@ class Create extends Action
if ($relation instanceof Document) {
$relation = $this->removeReadonlyAttributes($relation, $isAPIKey || $isPrivilegedUser);
$current = $authorization->skip(
$current = Authorization::skip(
fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(), $relation->getId())
);
@@ -375,7 +369,7 @@ class Create extends Action
// Handle transaction staging
if ($transactionId !== null) {
$transaction = ($isAPIKey || $isPrivilegedUser)
? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
: $dbForProject->getDocument('transactions', $transactionId);
if ($transaction->isEmpty()) {
throw new Exception(Exception::TRANSACTION_NOT_FOUND, params: [$transactionId]);
@@ -474,7 +468,6 @@ class Create extends Action
document: $document,
dbForProject: $dbForProject,
collectionsCache: $collectionsCache,
authorization: $authorization
);
}
@@ -83,7 +83,6 @@ class Delete extends Action
->inject('queueForStatsUsage')
->inject('transactionState')
->inject('plan')
->inject('authorization')
->callback($this->action(...));
}
@@ -98,19 +97,18 @@ class Delete extends Action
Event $queueForEvents,
StatsUsage $queueForStatsUsage,
TransactionState $transactionState,
array $plan,
Authorization $authorization
array $plan
): void {
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
$collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception($this->getParentNotFoundException(), params: [$collectionId]);
@@ -123,7 +121,7 @@ class Delete extends Action
// Use transaction-aware document retrieval to see changes from same transaction
$document = $transactionState->getDocument($collectionTableId, $documentId, $transactionId);
} else {
$document = $authorization->skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId));
$document = Authorization::skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId));
}
if ($document->isEmpty()) {
@@ -133,7 +131,7 @@ class Delete extends Action
// Handle transaction staging
if ($transactionId !== null) {
$transaction = ($isAPIKey || $isPrivilegedUser)
? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
: $dbForProject->getDocument('transactions', $transactionId);
if ($transaction->isEmpty()) {
throw new Exception(Exception::TRANSACTION_NOT_FOUND, params: [$transactionId]);
@@ -207,7 +205,6 @@ class Delete extends Action
document: $document,
dbForProject: $dbForProject,
collectionsCache: $collectionsCache,
authorization: $authorization
);
$queueForStatsUsage
@@ -70,21 +70,20 @@ class Get extends Action
->inject('dbForProject')
->inject('queueForStatsUsage')
->inject('transactionState')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $documentId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionState $transactionState, Authorization $authorization): void
public function action(string $databaseId, string $collectionId, string $documentId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionState $transactionState): void
{
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
$collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception($this->getParentNotFoundException(), params: [$collectionId]);
@@ -126,7 +125,6 @@ class Get extends Action
document: $document,
dbForProject: $dbForProject,
collectionsCache: $collectionsCache,
authorization: $authorization,
operations: $operations
);

Some files were not shown because too many files have changed in this diff Show More