├── .gitignore ├── .php_cs.dist ├── README.md ├── app.php ├── composer.json ├── composer.lock ├── config.dist.php ├── config └── services.yaml └── src ├── Command ├── Assign │ └── AssignWithGroupCommand.php ├── Group │ └── ListGroupMembers.php ├── Iam │ ├── CreateServiceAccountCommand.php │ └── ListServiceAccountsCommand.php ├── Rclone │ └── RcloneConfigCommand.php └── Teamdrive │ ├── CreateTeamdriveCommand.php │ └── ListTeamdrivesCommand.php ├── Service ├── GoogleDriveService.php ├── GoogleGroupService.php ├── GoogleIamService.php ├── RcloneConfigService.php └── RequestQueue.php └── Struct ├── ServiceAccountGroup.php └── User.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | .idea/ 3 | *.json 4 | config.php 5 | !composer.json 6 | rclone.conf 7 | .php_cs.cache 8 | 9 | # Created by https://www.gitignore.io/api/macos 10 | # Edit at https://www.gitignore.io/?templates=macos 11 | 12 | ### macOS ### 13 | # General 14 | .DS_Store 15 | .AppleDouble 16 | .LSOverride 17 | 18 | # Icon must end with two \r 19 | Icon 20 | 21 | # Thumbnails 22 | ._* 23 | 24 | # Files that might appear in the root of a volume 25 | .DocumentRevisions-V100 26 | .fseventsd 27 | .Spotlight-V100 28 | .TemporaryItems 29 | .Trashes 30 | .VolumeIcon.icns 31 | .com.apple.timemachine.donotpresent 32 | 33 | # Directories potentially created on remote AFP share 34 | .AppleDB 35 | .AppleDesktop 36 | Network Trash Folder 37 | Temporary Items 38 | .apdisk 39 | 40 | # End of https://www.gitignore.io/api/macos -------------------------------------------------------------------------------- /.php_cs.dist: -------------------------------------------------------------------------------- 1 | setUsingCache(false) 7 | ->setRules([ 8 | '@PSR2' => true, 9 | '@Symfony' => true, 10 | 'single_blank_line_before_namespace' => true, 11 | 'function_typehint_space' => true, 12 | 'declare_strict_types' => true, 13 | 'function_declaration' => true, 14 | 'no_empty_phpdoc' => true, 15 | 'no_empty_comment' => true, 16 | 'no_useless_return' => true, 17 | 'ordered_imports' => true, 18 | 'phpdoc_add_missing_param_annotation' => [ 19 | 'only_untyped' => false 20 | ], 21 | 'binary_operator_spaces' => [ 22 | 'align_double_arrow' => true 23 | ], 24 | 'phpdoc_no_empty_return' => true, 25 | 'phpdoc_order' => true, 26 | 'phpdoc_types' => true, 27 | 'phpdoc_scalar' => true, 28 | 'phpdoc_no_package' => false, 29 | 'trailing_comma_in_multiline_array' => false, 30 | 'single_quote' => true, 31 | 'return_type_declaration' => true, 32 | 33 | 'class_attributes_separation' => [], 34 | 'no_blank_lines_after_class_opening' => true, 35 | 'phpdoc_return_self_reference' => true, 36 | 'phpdoc_trim' => true, 37 | 'blank_line_before_return' => true, 38 | 'whitespace_after_comma_in_array' => true, 39 | 'is_null' => true, 40 | 'no_spaces_after_function_name' => true, 41 | 'no_trailing_whitespace' => true, 42 | 'no_trailing_whitespace_in_comment' => true, 43 | 'strict_comparison' => true, 44 | 'no_blank_lines_after_phpdoc' => true, 45 | 'phpdoc_single_line_var_spacing' => true, 46 | 'phpdoc_var_without_name' => true, 47 | 'cast_spaces' => true, 48 | 'class_definition' => array('singleLine' => true), 49 | 'self_accessor' => true, 50 | 'logical_operators' => true, 51 | 'no_useless_else' => true, 52 | 'ordered_class_elements' => true, 53 | 'phpdoc_summary' => false, 54 | 'blank_line_after_opening_tag' => false, 55 | 'concat_space' => ['spacing' => 'one'], 56 | 'array_syntax' => ['syntax' => 'short'], 57 | 'yoda_style' => ['equal' => false, 'identical' => false, 'less_and_greater' => false], 58 | ]) 59 | ->setRiskyAllowed(true) 60 | ->setFinder($finder); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Teamdrive Manager (with extra Features) 2 | 3 | ## Needs 4 | - PHP 7.2 5 | - GSuite Account (With a ton of permissions) 6 | 7 | ### Google Setup 8 | - Go to the Dev Console of Google (https://console.developers.google.com/) 9 | - Create a new API Project 10 | - Name it as you want in this tutorial I name it "TeamdriveManager" 11 | - After its created select it 12 | - Click on "Enable APIs" 13 | - Enable the Admin SDK 14 | - Enable the Identity and Access Management (IAM) API 15 | - Enable the Google Drive API 16 | - Click on "Credentials" 17 | - "Create Credentials" 18 | - "Service Account Key" 19 | - Create a new Service Account 20 | - As name you should use "TeamdriveManager-Impersonate" 21 | - Dont select a Role 22 | - As Type select JSON 23 | - When asked say "Create without Role" 24 | - You will now download a JSON File. DONT LOSE THE JSON FILE! 25 | - Click on "Manage Service Accounts" 26 | - click on the mail address of the Service Account 27 | - Click Edit in the Top 28 | - Click on "Show Domain-wide delegation" 29 | - Enable "Enable G Suite Domain-wide Delegation" 30 | - As Product name just use the Project name again 31 | - Press Save 32 | - copy the Client ID to some notepad.exe or so 33 | - Go to the Admin Console (admin.google.com/YOURDOMAIN) 34 | - Go into "Security" (or use the search bar) 35 | - Select "Show more" and then "Advanced settings" 36 | - Select "Manage API client access" in the "Authentication" section 37 | - In the "Client Name" field enter the service account’s "Client ID" 38 | - In the next field, "One or More API Scopes", enter the following 39 | - `https://www.googleapis.com/auth/admin.directory.group,https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/drive` 40 | 41 | ### PHP Setup 42 | - Clone the Git Repo 43 | - go into the Folder 44 | - run composer 45 | - "composer install" 46 | - copy the config.dist.php file to config.php 47 | - edit the config file to your needs 48 | - put the downloaded JSON file in the same directory and change the name in the config to it 49 | - change the subject to your google account email 50 | - change the domain to your google account domain 51 | - put all users you want with their corresponding role in the users array 52 | - you can empty the Blacklist as you mostly dont need it 53 | - teamDriveNameBegin is the Prefix all your Drives that should be Managed have like "Fionera - " 54 | - The IAM Section is for Service Account Stuff and currently not needed 55 | - Now you can use "php app.php assign:mail" for assigning all Email Addresses with their roles to the Teamdrives that match the prefix 56 | -------------------------------------------------------------------------------- /app.php: -------------------------------------------------------------------------------- 1 | setParameter('config', $config); 18 | 19 | $users = User::fromConfig($config); 20 | $containerBuilder->setParameter('users', $users); 21 | unset($users); 22 | 23 | $containerBuilder->register(Google_Client::class)->setSynthetic(true); 24 | 25 | $containerBuilder 26 | ->register(Google_Service_Drive::class, Google_Service_Drive::class) 27 | ->addArgument(new Reference(Google_Client::class)); 28 | 29 | $containerBuilder 30 | ->register(Google_Service_Directory::class, Google_Service_Directory::class) 31 | ->addArgument(new Reference(Google_Client::class)); 32 | 33 | $containerBuilder 34 | ->register(Google_Service_Iam::class, Google_Service_Iam::class) 35 | ->addArgument(new Reference(Google_Client::class)); 36 | 37 | $loader = new YamlFileLoader($containerBuilder, new FileLocator(__DIR__ . DIRECTORY_SEPARATOR . 'config')); 38 | $loader->load('services.yaml'); 39 | 40 | $containerBuilder->addCompilerPass(new AddConsoleCommandPass()); 41 | 42 | $containerBuilder->compile(); 43 | 44 | $client = new Google_Client(); 45 | $client->useApplicationDefaultCredentials(); 46 | $client->setSubject($config['subject']); 47 | $client->setScopes([Google_Service_Drive::DRIVE, Google_Service_Directory::ADMIN_DIRECTORY_GROUP, Google_Service_Iam::CLOUD_PLATFORM]); 48 | $client->setDefer(true); 49 | $containerBuilder->set(Google_Client::class, $client); 50 | 51 | $container = $containerBuilder; 52 | unset($containerBuilder); 53 | 54 | $loop = React\EventLoop\Factory::create(); 55 | 56 | $application = new Application(); 57 | 58 | /** @var \Symfony\Component\Console\CommandLoader\CommandLoaderInterface $commandLoader */ 59 | $commandLoader = $container->get('console.command_loader'); 60 | $application->setCommandLoader($commandLoader); 61 | 62 | $loop->run(); 63 | 64 | $application->run(); 65 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "google/apiclient": "^2.2", 4 | "clue/mq-react": "^1.1", 5 | "react/event-loop": "^1.0", 6 | "react/stream": "^1.0", 7 | "symfony/console": "^4.1", 8 | "symfony/dependency-injection": "^4.1", 9 | "symfony/config": "^4.1", 10 | "symfony/yaml": "^4.1" 11 | }, 12 | "autoload": { 13 | "psr-4": { 14 | "TeamdriveManager\\": "src/" 15 | } 16 | }, 17 | "require-dev": { 18 | "friendsofphp/php-cs-fixer": "^2.14" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "ab6e2d7af618eaf017b8bbc6b2d58bb3", 8 | "packages": [ 9 | { 10 | "name": "clue/mq-react", 11 | "version": "v1.1.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/clue/php-mq-react.git", 15 | "reference": "f012cb0735b018acdda66db9438d028b93a93f24" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/clue/php-mq-react/zipball/f012cb0735b018acdda66db9438d028b93a93f24", 20 | "reference": "f012cb0735b018acdda66db9438d028b93a93f24", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=5.3", 25 | "react/promise": "^2.2.1 || ^1.2.1" 26 | }, 27 | "require-dev": { 28 | "clue/block-react": "^1.0", 29 | "clue/buzz-react": "^2.0", 30 | "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35", 31 | "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3" 32 | }, 33 | "type": "library", 34 | "autoload": { 35 | "psr-4": { 36 | "Clue\\React\\Mq\\": "src/" 37 | } 38 | }, 39 | "notification-url": "https://packagist.org/downloads/", 40 | "license": [ 41 | "MIT" 42 | ], 43 | "authors": [ 44 | { 45 | "name": "Christian Lück", 46 | "email": "christian@lueck.tv" 47 | } 48 | ], 49 | "description": "Mini Queue, the lightweight in-memory message queue to concurrently do many (but not too many) things at once, built on top of ReactPHP", 50 | "homepage": "https://github.com/clue/php-mq-react", 51 | "keywords": [ 52 | "Mini Queue", 53 | "async", 54 | "concurrency", 55 | "job", 56 | "message", 57 | "message queue", 58 | "queue", 59 | "rate limit", 60 | "reactphp", 61 | "throttle", 62 | "worker" 63 | ], 64 | "time": "2018-04-30T09:46:20+00:00" 65 | }, 66 | { 67 | "name": "evenement/evenement", 68 | "version": "v3.0.1", 69 | "source": { 70 | "type": "git", 71 | "url": "https://github.com/igorw/evenement.git", 72 | "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7" 73 | }, 74 | "dist": { 75 | "type": "zip", 76 | "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7", 77 | "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7", 78 | "shasum": "" 79 | }, 80 | "require": { 81 | "php": ">=7.0" 82 | }, 83 | "require-dev": { 84 | "phpunit/phpunit": "^6.0" 85 | }, 86 | "type": "library", 87 | "autoload": { 88 | "psr-0": { 89 | "Evenement": "src" 90 | } 91 | }, 92 | "notification-url": "https://packagist.org/downloads/", 93 | "license": [ 94 | "MIT" 95 | ], 96 | "authors": [ 97 | { 98 | "name": "Igor Wiedler", 99 | "email": "igor@wiedler.ch" 100 | } 101 | ], 102 | "description": "Événement is a very simple event dispatching library for PHP", 103 | "keywords": [ 104 | "event-dispatcher", 105 | "event-emitter" 106 | ], 107 | "time": "2017-07-23T21:35:13+00:00" 108 | }, 109 | { 110 | "name": "firebase/php-jwt", 111 | "version": "v5.0.0", 112 | "source": { 113 | "type": "git", 114 | "url": "https://github.com/firebase/php-jwt.git", 115 | "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e" 116 | }, 117 | "dist": { 118 | "type": "zip", 119 | "url": "https://api.github.com/repos/firebase/php-jwt/zipball/9984a4d3a32ae7673d6971ea00bae9d0a1abba0e", 120 | "reference": "9984a4d3a32ae7673d6971ea00bae9d0a1abba0e", 121 | "shasum": "" 122 | }, 123 | "require": { 124 | "php": ">=5.3.0" 125 | }, 126 | "require-dev": { 127 | "phpunit/phpunit": " 4.8.35" 128 | }, 129 | "type": "library", 130 | "autoload": { 131 | "psr-4": { 132 | "Firebase\\JWT\\": "src" 133 | } 134 | }, 135 | "notification-url": "https://packagist.org/downloads/", 136 | "license": [ 137 | "BSD-3-Clause" 138 | ], 139 | "authors": [ 140 | { 141 | "name": "Neuman Vong", 142 | "email": "neuman+pear@twilio.com", 143 | "role": "Developer" 144 | }, 145 | { 146 | "name": "Anant Narayanan", 147 | "email": "anant@php.net", 148 | "role": "Developer" 149 | } 150 | ], 151 | "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", 152 | "homepage": "https://github.com/firebase/php-jwt", 153 | "time": "2017-06-27T22:17:23+00:00" 154 | }, 155 | { 156 | "name": "google/apiclient", 157 | "version": "v2.2.2", 158 | "source": { 159 | "type": "git", 160 | "url": "https://github.com/googleapis/google-api-php-client.git", 161 | "reference": "4e0fd83510e579043e10e565528b323b7c2b3c81" 162 | }, 163 | "dist": { 164 | "type": "zip", 165 | "url": "https://api.github.com/repos/googleapis/google-api-php-client/zipball/4e0fd83510e579043e10e565528b323b7c2b3c81", 166 | "reference": "4e0fd83510e579043e10e565528b323b7c2b3c81", 167 | "shasum": "" 168 | }, 169 | "require": { 170 | "firebase/php-jwt": "~2.0|~3.0|~4.0|~5.0", 171 | "google/apiclient-services": "~0.13", 172 | "google/auth": "^1.0", 173 | "guzzlehttp/guzzle": "~5.3.1|~6.0", 174 | "guzzlehttp/psr7": "^1.2", 175 | "monolog/monolog": "^1.17", 176 | "php": ">=5.4", 177 | "phpseclib/phpseclib": "~0.3.10|~2.0" 178 | }, 179 | "require-dev": { 180 | "cache/filesystem-adapter": "^0.3.2", 181 | "phpunit/phpunit": "~4.8.36", 182 | "squizlabs/php_codesniffer": "~2.3", 183 | "symfony/css-selector": "~2.1", 184 | "symfony/dom-crawler": "~2.1" 185 | }, 186 | "suggest": { 187 | "cache/filesystem-adapter": "For caching certs and tokens (using Google_Client::setCache)" 188 | }, 189 | "type": "library", 190 | "extra": { 191 | "branch-alias": { 192 | "dev-master": "2.x-dev" 193 | } 194 | }, 195 | "autoload": { 196 | "psr-0": { 197 | "Google_": "src/" 198 | }, 199 | "classmap": [ 200 | "src/Google/Service/" 201 | ] 202 | }, 203 | "notification-url": "https://packagist.org/downloads/", 204 | "license": [ 205 | "Apache-2.0" 206 | ], 207 | "description": "Client library for Google APIs", 208 | "homepage": "http://developers.google.com/api-client-library/php", 209 | "keywords": [ 210 | "google" 211 | ], 212 | "time": "2018-06-20T15:52:20+00:00" 213 | }, 214 | { 215 | "name": "google/apiclient-services", 216 | "version": "v0.92", 217 | "source": { 218 | "type": "git", 219 | "url": "https://github.com/googleapis/google-api-php-client-services.git", 220 | "reference": "8d8f7faebe21e4cbf94921c97af6e02daca7cefb" 221 | }, 222 | "dist": { 223 | "type": "zip", 224 | "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/8d8f7faebe21e4cbf94921c97af6e02daca7cefb", 225 | "reference": "8d8f7faebe21e4cbf94921c97af6e02daca7cefb", 226 | "shasum": "" 227 | }, 228 | "require": { 229 | "php": ">=5.4" 230 | }, 231 | "require-dev": { 232 | "phpunit/phpunit": "~4.8" 233 | }, 234 | "type": "library", 235 | "autoload": { 236 | "psr-0": { 237 | "Google_Service_": "src" 238 | } 239 | }, 240 | "notification-url": "https://packagist.org/downloads/", 241 | "license": [ 242 | "Apache-2.0" 243 | ], 244 | "description": "Client library for Google APIs", 245 | "homepage": "http://developers.google.com/api-client-library/php", 246 | "keywords": [ 247 | "google" 248 | ], 249 | "time": "2019-03-31T00:23:05+00:00" 250 | }, 251 | { 252 | "name": "google/auth", 253 | "version": "v1.4.0", 254 | "source": { 255 | "type": "git", 256 | "url": "https://github.com/googleapis/google-auth-library-php.git", 257 | "reference": "196237248e636a3554a7d9e4dfddeb97f450ab5c" 258 | }, 259 | "dist": { 260 | "type": "zip", 261 | "url": "https://api.github.com/repos/googleapis/google-auth-library-php/zipball/196237248e636a3554a7d9e4dfddeb97f450ab5c", 262 | "reference": "196237248e636a3554a7d9e4dfddeb97f450ab5c", 263 | "shasum": "" 264 | }, 265 | "require": { 266 | "firebase/php-jwt": "~2.0|~3.0|~4.0|~5.0", 267 | "guzzlehttp/guzzle": "~5.3.1|~6.0", 268 | "guzzlehttp/psr7": "^1.2", 269 | "php": ">=5.4", 270 | "psr/cache": "^1.0", 271 | "psr/http-message": "^1.0" 272 | }, 273 | "require-dev": { 274 | "friendsofphp/php-cs-fixer": "^1.11", 275 | "guzzlehttp/promises": "0.1.1|^1.3", 276 | "phpunit/phpunit": "^4.8.36|^5.7", 277 | "sebastian/comparator": ">=1.2.3" 278 | }, 279 | "type": "library", 280 | "autoload": { 281 | "psr-4": { 282 | "Google\\Auth\\": "src" 283 | } 284 | }, 285 | "notification-url": "https://packagist.org/downloads/", 286 | "license": [ 287 | "Apache-2.0" 288 | ], 289 | "description": "Google Auth Library for PHP", 290 | "homepage": "http://github.com/google/google-auth-library-php", 291 | "keywords": [ 292 | "Authentication", 293 | "google", 294 | "oauth2" 295 | ], 296 | "time": "2018-09-17T20:29:21+00:00" 297 | }, 298 | { 299 | "name": "guzzlehttp/guzzle", 300 | "version": "6.3.3", 301 | "source": { 302 | "type": "git", 303 | "url": "https://github.com/guzzle/guzzle.git", 304 | "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" 305 | }, 306 | "dist": { 307 | "type": "zip", 308 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", 309 | "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", 310 | "shasum": "" 311 | }, 312 | "require": { 313 | "guzzlehttp/promises": "^1.0", 314 | "guzzlehttp/psr7": "^1.4", 315 | "php": ">=5.5" 316 | }, 317 | "require-dev": { 318 | "ext-curl": "*", 319 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", 320 | "psr/log": "^1.0" 321 | }, 322 | "suggest": { 323 | "psr/log": "Required for using the Log middleware" 324 | }, 325 | "type": "library", 326 | "extra": { 327 | "branch-alias": { 328 | "dev-master": "6.3-dev" 329 | } 330 | }, 331 | "autoload": { 332 | "files": [ 333 | "src/functions_include.php" 334 | ], 335 | "psr-4": { 336 | "GuzzleHttp\\": "src/" 337 | } 338 | }, 339 | "notification-url": "https://packagist.org/downloads/", 340 | "license": [ 341 | "MIT" 342 | ], 343 | "authors": [ 344 | { 345 | "name": "Michael Dowling", 346 | "email": "mtdowling@gmail.com", 347 | "homepage": "https://github.com/mtdowling" 348 | } 349 | ], 350 | "description": "Guzzle is a PHP HTTP client library", 351 | "homepage": "http://guzzlephp.org/", 352 | "keywords": [ 353 | "client", 354 | "curl", 355 | "framework", 356 | "http", 357 | "http client", 358 | "rest", 359 | "web service" 360 | ], 361 | "time": "2018-04-22T15:46:56+00:00" 362 | }, 363 | { 364 | "name": "guzzlehttp/promises", 365 | "version": "v1.3.1", 366 | "source": { 367 | "type": "git", 368 | "url": "https://github.com/guzzle/promises.git", 369 | "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" 370 | }, 371 | "dist": { 372 | "type": "zip", 373 | "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", 374 | "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", 375 | "shasum": "" 376 | }, 377 | "require": { 378 | "php": ">=5.5.0" 379 | }, 380 | "require-dev": { 381 | "phpunit/phpunit": "^4.0" 382 | }, 383 | "type": "library", 384 | "extra": { 385 | "branch-alias": { 386 | "dev-master": "1.4-dev" 387 | } 388 | }, 389 | "autoload": { 390 | "psr-4": { 391 | "GuzzleHttp\\Promise\\": "src/" 392 | }, 393 | "files": [ 394 | "src/functions_include.php" 395 | ] 396 | }, 397 | "notification-url": "https://packagist.org/downloads/", 398 | "license": [ 399 | "MIT" 400 | ], 401 | "authors": [ 402 | { 403 | "name": "Michael Dowling", 404 | "email": "mtdowling@gmail.com", 405 | "homepage": "https://github.com/mtdowling" 406 | } 407 | ], 408 | "description": "Guzzle promises library", 409 | "keywords": [ 410 | "promise" 411 | ], 412 | "time": "2016-12-20T10:07:11+00:00" 413 | }, 414 | { 415 | "name": "guzzlehttp/psr7", 416 | "version": "1.5.2", 417 | "source": { 418 | "type": "git", 419 | "url": "https://github.com/guzzle/psr7.git", 420 | "reference": "9f83dded91781a01c63574e387eaa769be769115" 421 | }, 422 | "dist": { 423 | "type": "zip", 424 | "url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115", 425 | "reference": "9f83dded91781a01c63574e387eaa769be769115", 426 | "shasum": "" 427 | }, 428 | "require": { 429 | "php": ">=5.4.0", 430 | "psr/http-message": "~1.0", 431 | "ralouphie/getallheaders": "^2.0.5" 432 | }, 433 | "provide": { 434 | "psr/http-message-implementation": "1.0" 435 | }, 436 | "require-dev": { 437 | "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" 438 | }, 439 | "type": "library", 440 | "extra": { 441 | "branch-alias": { 442 | "dev-master": "1.5-dev" 443 | } 444 | }, 445 | "autoload": { 446 | "psr-4": { 447 | "GuzzleHttp\\Psr7\\": "src/" 448 | }, 449 | "files": [ 450 | "src/functions_include.php" 451 | ] 452 | }, 453 | "notification-url": "https://packagist.org/downloads/", 454 | "license": [ 455 | "MIT" 456 | ], 457 | "authors": [ 458 | { 459 | "name": "Michael Dowling", 460 | "email": "mtdowling@gmail.com", 461 | "homepage": "https://github.com/mtdowling" 462 | }, 463 | { 464 | "name": "Tobias Schultze", 465 | "homepage": "https://github.com/Tobion" 466 | } 467 | ], 468 | "description": "PSR-7 message implementation that also provides common utility methods", 469 | "keywords": [ 470 | "http", 471 | "message", 472 | "psr-7", 473 | "request", 474 | "response", 475 | "stream", 476 | "uri", 477 | "url" 478 | ], 479 | "time": "2018-12-04T20:46:45+00:00" 480 | }, 481 | { 482 | "name": "monolog/monolog", 483 | "version": "1.24.0", 484 | "source": { 485 | "type": "git", 486 | "url": "https://github.com/Seldaek/monolog.git", 487 | "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266" 488 | }, 489 | "dist": { 490 | "type": "zip", 491 | "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", 492 | "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", 493 | "shasum": "" 494 | }, 495 | "require": { 496 | "php": ">=5.3.0", 497 | "psr/log": "~1.0" 498 | }, 499 | "provide": { 500 | "psr/log-implementation": "1.0.0" 501 | }, 502 | "require-dev": { 503 | "aws/aws-sdk-php": "^2.4.9 || ^3.0", 504 | "doctrine/couchdb": "~1.0@dev", 505 | "graylog2/gelf-php": "~1.0", 506 | "jakub-onderka/php-parallel-lint": "0.9", 507 | "php-amqplib/php-amqplib": "~2.4", 508 | "php-console/php-console": "^3.1.3", 509 | "phpunit/phpunit": "~4.5", 510 | "phpunit/phpunit-mock-objects": "2.3.0", 511 | "ruflin/elastica": ">=0.90 <3.0", 512 | "sentry/sentry": "^0.13", 513 | "swiftmailer/swiftmailer": "^5.3|^6.0" 514 | }, 515 | "suggest": { 516 | "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", 517 | "doctrine/couchdb": "Allow sending log messages to a CouchDB server", 518 | "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", 519 | "ext-mongo": "Allow sending log messages to a MongoDB server", 520 | "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", 521 | "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", 522 | "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", 523 | "php-console/php-console": "Allow sending log messages to Google Chrome", 524 | "rollbar/rollbar": "Allow sending log messages to Rollbar", 525 | "ruflin/elastica": "Allow sending log messages to an Elastic Search server", 526 | "sentry/sentry": "Allow sending log messages to a Sentry server" 527 | }, 528 | "type": "library", 529 | "extra": { 530 | "branch-alias": { 531 | "dev-master": "2.0.x-dev" 532 | } 533 | }, 534 | "autoload": { 535 | "psr-4": { 536 | "Monolog\\": "src/Monolog" 537 | } 538 | }, 539 | "notification-url": "https://packagist.org/downloads/", 540 | "license": [ 541 | "MIT" 542 | ], 543 | "authors": [ 544 | { 545 | "name": "Jordi Boggiano", 546 | "email": "j.boggiano@seld.be", 547 | "homepage": "http://seld.be" 548 | } 549 | ], 550 | "description": "Sends your logs to files, sockets, inboxes, databases and various web services", 551 | "homepage": "http://github.com/Seldaek/monolog", 552 | "keywords": [ 553 | "log", 554 | "logging", 555 | "psr-3" 556 | ], 557 | "time": "2018-11-05T09:00:11+00:00" 558 | }, 559 | { 560 | "name": "phpseclib/phpseclib", 561 | "version": "2.0.15", 562 | "source": { 563 | "type": "git", 564 | "url": "https://github.com/phpseclib/phpseclib.git", 565 | "reference": "11cf67cf78dc4acb18dc9149a57be4aee5036ce0" 566 | }, 567 | "dist": { 568 | "type": "zip", 569 | "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/11cf67cf78dc4acb18dc9149a57be4aee5036ce0", 570 | "reference": "11cf67cf78dc4acb18dc9149a57be4aee5036ce0", 571 | "shasum": "" 572 | }, 573 | "require": { 574 | "php": ">=5.3.3" 575 | }, 576 | "require-dev": { 577 | "phing/phing": "~2.7", 578 | "phpunit/phpunit": "^4.8.35|^5.7|^6.0", 579 | "sami/sami": "~2.0", 580 | "squizlabs/php_codesniffer": "~2.0" 581 | }, 582 | "suggest": { 583 | "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", 584 | "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", 585 | "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", 586 | "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." 587 | }, 588 | "type": "library", 589 | "autoload": { 590 | "files": [ 591 | "phpseclib/bootstrap.php" 592 | ], 593 | "psr-4": { 594 | "phpseclib\\": "phpseclib/" 595 | } 596 | }, 597 | "notification-url": "https://packagist.org/downloads/", 598 | "license": [ 599 | "MIT" 600 | ], 601 | "authors": [ 602 | { 603 | "name": "Jim Wigginton", 604 | "email": "terrafrost@php.net", 605 | "role": "Lead Developer" 606 | }, 607 | { 608 | "name": "Patrick Monnerat", 609 | "email": "pm@datasphere.ch", 610 | "role": "Developer" 611 | }, 612 | { 613 | "name": "Andreas Fischer", 614 | "email": "bantu@phpbb.com", 615 | "role": "Developer" 616 | }, 617 | { 618 | "name": "Hans-Jürgen Petrich", 619 | "email": "petrich@tronic-media.com", 620 | "role": "Developer" 621 | }, 622 | { 623 | "name": "Graham Campbell", 624 | "email": "graham@alt-three.com", 625 | "role": "Developer" 626 | } 627 | ], 628 | "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", 629 | "homepage": "http://phpseclib.sourceforge.net", 630 | "keywords": [ 631 | "BigInteger", 632 | "aes", 633 | "asn.1", 634 | "asn1", 635 | "blowfish", 636 | "crypto", 637 | "cryptography", 638 | "encryption", 639 | "rsa", 640 | "security", 641 | "sftp", 642 | "signature", 643 | "signing", 644 | "ssh", 645 | "twofish", 646 | "x.509", 647 | "x509" 648 | ], 649 | "time": "2019-03-10T16:53:45+00:00" 650 | }, 651 | { 652 | "name": "psr/cache", 653 | "version": "1.0.1", 654 | "source": { 655 | "type": "git", 656 | "url": "https://github.com/php-fig/cache.git", 657 | "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" 658 | }, 659 | "dist": { 660 | "type": "zip", 661 | "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", 662 | "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", 663 | "shasum": "" 664 | }, 665 | "require": { 666 | "php": ">=5.3.0" 667 | }, 668 | "type": "library", 669 | "extra": { 670 | "branch-alias": { 671 | "dev-master": "1.0.x-dev" 672 | } 673 | }, 674 | "autoload": { 675 | "psr-4": { 676 | "Psr\\Cache\\": "src/" 677 | } 678 | }, 679 | "notification-url": "https://packagist.org/downloads/", 680 | "license": [ 681 | "MIT" 682 | ], 683 | "authors": [ 684 | { 685 | "name": "PHP-FIG", 686 | "homepage": "http://www.php-fig.org/" 687 | } 688 | ], 689 | "description": "Common interface for caching libraries", 690 | "keywords": [ 691 | "cache", 692 | "psr", 693 | "psr-6" 694 | ], 695 | "time": "2016-08-06T20:24:11+00:00" 696 | }, 697 | { 698 | "name": "psr/container", 699 | "version": "1.0.0", 700 | "source": { 701 | "type": "git", 702 | "url": "https://github.com/php-fig/container.git", 703 | "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" 704 | }, 705 | "dist": { 706 | "type": "zip", 707 | "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", 708 | "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", 709 | "shasum": "" 710 | }, 711 | "require": { 712 | "php": ">=5.3.0" 713 | }, 714 | "type": "library", 715 | "extra": { 716 | "branch-alias": { 717 | "dev-master": "1.0.x-dev" 718 | } 719 | }, 720 | "autoload": { 721 | "psr-4": { 722 | "Psr\\Container\\": "src/" 723 | } 724 | }, 725 | "notification-url": "https://packagist.org/downloads/", 726 | "license": [ 727 | "MIT" 728 | ], 729 | "authors": [ 730 | { 731 | "name": "PHP-FIG", 732 | "homepage": "http://www.php-fig.org/" 733 | } 734 | ], 735 | "description": "Common Container Interface (PHP FIG PSR-11)", 736 | "homepage": "https://github.com/php-fig/container", 737 | "keywords": [ 738 | "PSR-11", 739 | "container", 740 | "container-interface", 741 | "container-interop", 742 | "psr" 743 | ], 744 | "time": "2017-02-14T16:28:37+00:00" 745 | }, 746 | { 747 | "name": "psr/http-message", 748 | "version": "1.0.1", 749 | "source": { 750 | "type": "git", 751 | "url": "https://github.com/php-fig/http-message.git", 752 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" 753 | }, 754 | "dist": { 755 | "type": "zip", 756 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", 757 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", 758 | "shasum": "" 759 | }, 760 | "require": { 761 | "php": ">=5.3.0" 762 | }, 763 | "type": "library", 764 | "extra": { 765 | "branch-alias": { 766 | "dev-master": "1.0.x-dev" 767 | } 768 | }, 769 | "autoload": { 770 | "psr-4": { 771 | "Psr\\Http\\Message\\": "src/" 772 | } 773 | }, 774 | "notification-url": "https://packagist.org/downloads/", 775 | "license": [ 776 | "MIT" 777 | ], 778 | "authors": [ 779 | { 780 | "name": "PHP-FIG", 781 | "homepage": "http://www.php-fig.org/" 782 | } 783 | ], 784 | "description": "Common interface for HTTP messages", 785 | "homepage": "https://github.com/php-fig/http-message", 786 | "keywords": [ 787 | "http", 788 | "http-message", 789 | "psr", 790 | "psr-7", 791 | "request", 792 | "response" 793 | ], 794 | "time": "2016-08-06T14:39:51+00:00" 795 | }, 796 | { 797 | "name": "psr/log", 798 | "version": "1.1.0", 799 | "source": { 800 | "type": "git", 801 | "url": "https://github.com/php-fig/log.git", 802 | "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" 803 | }, 804 | "dist": { 805 | "type": "zip", 806 | "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", 807 | "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", 808 | "shasum": "" 809 | }, 810 | "require": { 811 | "php": ">=5.3.0" 812 | }, 813 | "type": "library", 814 | "extra": { 815 | "branch-alias": { 816 | "dev-master": "1.0.x-dev" 817 | } 818 | }, 819 | "autoload": { 820 | "psr-4": { 821 | "Psr\\Log\\": "Psr/Log/" 822 | } 823 | }, 824 | "notification-url": "https://packagist.org/downloads/", 825 | "license": [ 826 | "MIT" 827 | ], 828 | "authors": [ 829 | { 830 | "name": "PHP-FIG", 831 | "homepage": "http://www.php-fig.org/" 832 | } 833 | ], 834 | "description": "Common interface for logging libraries", 835 | "homepage": "https://github.com/php-fig/log", 836 | "keywords": [ 837 | "log", 838 | "psr", 839 | "psr-3" 840 | ], 841 | "time": "2018-11-20T15:27:04+00:00" 842 | }, 843 | { 844 | "name": "ralouphie/getallheaders", 845 | "version": "2.0.5", 846 | "source": { 847 | "type": "git", 848 | "url": "https://github.com/ralouphie/getallheaders.git", 849 | "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa" 850 | }, 851 | "dist": { 852 | "type": "zip", 853 | "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/5601c8a83fbba7ef674a7369456d12f1e0d0eafa", 854 | "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa", 855 | "shasum": "" 856 | }, 857 | "require": { 858 | "php": ">=5.3" 859 | }, 860 | "require-dev": { 861 | "phpunit/phpunit": "~3.7.0", 862 | "satooshi/php-coveralls": ">=1.0" 863 | }, 864 | "type": "library", 865 | "autoload": { 866 | "files": [ 867 | "src/getallheaders.php" 868 | ] 869 | }, 870 | "notification-url": "https://packagist.org/downloads/", 871 | "license": [ 872 | "MIT" 873 | ], 874 | "authors": [ 875 | { 876 | "name": "Ralph Khattar", 877 | "email": "ralph.khattar@gmail.com" 878 | } 879 | ], 880 | "description": "A polyfill for getallheaders.", 881 | "time": "2016-02-11T07:05:27+00:00" 882 | }, 883 | { 884 | "name": "react/event-loop", 885 | "version": "v1.1.0", 886 | "source": { 887 | "type": "git", 888 | "url": "https://github.com/reactphp/event-loop.git", 889 | "reference": "a0ecac955c67b57c40fe4a1b88a7cca1b58c982d" 890 | }, 891 | "dist": { 892 | "type": "zip", 893 | "url": "https://api.github.com/repos/reactphp/event-loop/zipball/a0ecac955c67b57c40fe4a1b88a7cca1b58c982d", 894 | "reference": "a0ecac955c67b57c40fe4a1b88a7cca1b58c982d", 895 | "shasum": "" 896 | }, 897 | "require": { 898 | "php": ">=5.3.0" 899 | }, 900 | "require-dev": { 901 | "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" 902 | }, 903 | "suggest": { 904 | "ext-event": "~1.0 for ExtEventLoop", 905 | "ext-pcntl": "For signal handling support when using the StreamSelectLoop", 906 | "ext-uv": "* for ExtUvLoop" 907 | }, 908 | "type": "library", 909 | "autoload": { 910 | "psr-4": { 911 | "React\\EventLoop\\": "src" 912 | } 913 | }, 914 | "notification-url": "https://packagist.org/downloads/", 915 | "license": [ 916 | "MIT" 917 | ], 918 | "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", 919 | "keywords": [ 920 | "asynchronous", 921 | "event-loop" 922 | ], 923 | "time": "2019-02-07T16:19:49+00:00" 924 | }, 925 | { 926 | "name": "react/promise", 927 | "version": "v2.7.1", 928 | "source": { 929 | "type": "git", 930 | "url": "https://github.com/reactphp/promise.git", 931 | "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d" 932 | }, 933 | "dist": { 934 | "type": "zip", 935 | "url": "https://api.github.com/repos/reactphp/promise/zipball/31ffa96f8d2ed0341a57848cbb84d88b89dd664d", 936 | "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d", 937 | "shasum": "" 938 | }, 939 | "require": { 940 | "php": ">=5.4.0" 941 | }, 942 | "require-dev": { 943 | "phpunit/phpunit": "~4.8" 944 | }, 945 | "type": "library", 946 | "autoload": { 947 | "psr-4": { 948 | "React\\Promise\\": "src/" 949 | }, 950 | "files": [ 951 | "src/functions_include.php" 952 | ] 953 | }, 954 | "notification-url": "https://packagist.org/downloads/", 955 | "license": [ 956 | "MIT" 957 | ], 958 | "authors": [ 959 | { 960 | "name": "Jan Sorgalla", 961 | "email": "jsorgalla@gmail.com" 962 | } 963 | ], 964 | "description": "A lightweight implementation of CommonJS Promises/A for PHP", 965 | "keywords": [ 966 | "promise", 967 | "promises" 968 | ], 969 | "time": "2019-01-07T21:25:54+00:00" 970 | }, 971 | { 972 | "name": "react/stream", 973 | "version": "v1.1.0", 974 | "source": { 975 | "type": "git", 976 | "url": "https://github.com/reactphp/stream.git", 977 | "reference": "50426855f7a77ddf43b9266c22320df5bf6c6ce6" 978 | }, 979 | "dist": { 980 | "type": "zip", 981 | "url": "https://api.github.com/repos/reactphp/stream/zipball/50426855f7a77ddf43b9266c22320df5bf6c6ce6", 982 | "reference": "50426855f7a77ddf43b9266c22320df5bf6c6ce6", 983 | "shasum": "" 984 | }, 985 | "require": { 986 | "evenement/evenement": "^3.0 || ^2.0 || ^1.0", 987 | "php": ">=5.3.8", 988 | "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5" 989 | }, 990 | "require-dev": { 991 | "clue/stream-filter": "~1.2", 992 | "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" 993 | }, 994 | "type": "library", 995 | "autoload": { 996 | "psr-4": { 997 | "React\\Stream\\": "src" 998 | } 999 | }, 1000 | "notification-url": "https://packagist.org/downloads/", 1001 | "license": [ 1002 | "MIT" 1003 | ], 1004 | "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", 1005 | "keywords": [ 1006 | "event-driven", 1007 | "io", 1008 | "non-blocking", 1009 | "pipe", 1010 | "reactphp", 1011 | "readable", 1012 | "stream", 1013 | "writable" 1014 | ], 1015 | "time": "2019-01-01T16:15:09+00:00" 1016 | }, 1017 | { 1018 | "name": "symfony/config", 1019 | "version": "v4.2.5", 1020 | "source": { 1021 | "type": "git", 1022 | "url": "https://github.com/symfony/config.git", 1023 | "reference": "0e745ead307d5dcd4e163e94a47ec04b1428943f" 1024 | }, 1025 | "dist": { 1026 | "type": "zip", 1027 | "url": "https://api.github.com/repos/symfony/config/zipball/0e745ead307d5dcd4e163e94a47ec04b1428943f", 1028 | "reference": "0e745ead307d5dcd4e163e94a47ec04b1428943f", 1029 | "shasum": "" 1030 | }, 1031 | "require": { 1032 | "php": "^7.1.3", 1033 | "symfony/filesystem": "~3.4|~4.0", 1034 | "symfony/polyfill-ctype": "~1.8" 1035 | }, 1036 | "conflict": { 1037 | "symfony/finder": "<3.4" 1038 | }, 1039 | "require-dev": { 1040 | "symfony/dependency-injection": "~3.4|~4.0", 1041 | "symfony/event-dispatcher": "~3.4|~4.0", 1042 | "symfony/finder": "~3.4|~4.0", 1043 | "symfony/yaml": "~3.4|~4.0" 1044 | }, 1045 | "suggest": { 1046 | "symfony/yaml": "To use the yaml reference dumper" 1047 | }, 1048 | "type": "library", 1049 | "extra": { 1050 | "branch-alias": { 1051 | "dev-master": "4.2-dev" 1052 | } 1053 | }, 1054 | "autoload": { 1055 | "psr-4": { 1056 | "Symfony\\Component\\Config\\": "" 1057 | }, 1058 | "exclude-from-classmap": [ 1059 | "/Tests/" 1060 | ] 1061 | }, 1062 | "notification-url": "https://packagist.org/downloads/", 1063 | "license": [ 1064 | "MIT" 1065 | ], 1066 | "authors": [ 1067 | { 1068 | "name": "Fabien Potencier", 1069 | "email": "fabien@symfony.com" 1070 | }, 1071 | { 1072 | "name": "Symfony Community", 1073 | "homepage": "https://symfony.com/contributors" 1074 | } 1075 | ], 1076 | "description": "Symfony Config Component", 1077 | "homepage": "https://symfony.com", 1078 | "time": "2019-04-01T14:03:25+00:00" 1079 | }, 1080 | { 1081 | "name": "symfony/console", 1082 | "version": "v4.2.5", 1083 | "source": { 1084 | "type": "git", 1085 | "url": "https://github.com/symfony/console.git", 1086 | "reference": "24206aff3efe6962593297e57ef697ebb220e384" 1087 | }, 1088 | "dist": { 1089 | "type": "zip", 1090 | "url": "https://api.github.com/repos/symfony/console/zipball/24206aff3efe6962593297e57ef697ebb220e384", 1091 | "reference": "24206aff3efe6962593297e57ef697ebb220e384", 1092 | "shasum": "" 1093 | }, 1094 | "require": { 1095 | "php": "^7.1.3", 1096 | "symfony/contracts": "^1.0", 1097 | "symfony/polyfill-mbstring": "~1.0" 1098 | }, 1099 | "conflict": { 1100 | "symfony/dependency-injection": "<3.4", 1101 | "symfony/process": "<3.3" 1102 | }, 1103 | "provide": { 1104 | "psr/log-implementation": "1.0" 1105 | }, 1106 | "require-dev": { 1107 | "psr/log": "~1.0", 1108 | "symfony/config": "~3.4|~4.0", 1109 | "symfony/dependency-injection": "~3.4|~4.0", 1110 | "symfony/event-dispatcher": "~3.4|~4.0", 1111 | "symfony/lock": "~3.4|~4.0", 1112 | "symfony/process": "~3.4|~4.0" 1113 | }, 1114 | "suggest": { 1115 | "psr/log": "For using the console logger", 1116 | "symfony/event-dispatcher": "", 1117 | "symfony/lock": "", 1118 | "symfony/process": "" 1119 | }, 1120 | "type": "library", 1121 | "extra": { 1122 | "branch-alias": { 1123 | "dev-master": "4.2-dev" 1124 | } 1125 | }, 1126 | "autoload": { 1127 | "psr-4": { 1128 | "Symfony\\Component\\Console\\": "" 1129 | }, 1130 | "exclude-from-classmap": [ 1131 | "/Tests/" 1132 | ] 1133 | }, 1134 | "notification-url": "https://packagist.org/downloads/", 1135 | "license": [ 1136 | "MIT" 1137 | ], 1138 | "authors": [ 1139 | { 1140 | "name": "Fabien Potencier", 1141 | "email": "fabien@symfony.com" 1142 | }, 1143 | { 1144 | "name": "Symfony Community", 1145 | "homepage": "https://symfony.com/contributors" 1146 | } 1147 | ], 1148 | "description": "Symfony Console Component", 1149 | "homepage": "https://symfony.com", 1150 | "time": "2019-04-01T07:32:59+00:00" 1151 | }, 1152 | { 1153 | "name": "symfony/contracts", 1154 | "version": "v1.0.2", 1155 | "source": { 1156 | "type": "git", 1157 | "url": "https://github.com/symfony/contracts.git", 1158 | "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf" 1159 | }, 1160 | "dist": { 1161 | "type": "zip", 1162 | "url": "https://api.github.com/repos/symfony/contracts/zipball/1aa7ab2429c3d594dd70689604b5cf7421254cdf", 1163 | "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf", 1164 | "shasum": "" 1165 | }, 1166 | "require": { 1167 | "php": "^7.1.3" 1168 | }, 1169 | "require-dev": { 1170 | "psr/cache": "^1.0", 1171 | "psr/container": "^1.0" 1172 | }, 1173 | "suggest": { 1174 | "psr/cache": "When using the Cache contracts", 1175 | "psr/container": "When using the Service contracts", 1176 | "symfony/cache-contracts-implementation": "", 1177 | "symfony/service-contracts-implementation": "", 1178 | "symfony/translation-contracts-implementation": "" 1179 | }, 1180 | "type": "library", 1181 | "extra": { 1182 | "branch-alias": { 1183 | "dev-master": "1.0-dev" 1184 | } 1185 | }, 1186 | "autoload": { 1187 | "psr-4": { 1188 | "Symfony\\Contracts\\": "" 1189 | }, 1190 | "exclude-from-classmap": [ 1191 | "**/Tests/" 1192 | ] 1193 | }, 1194 | "notification-url": "https://packagist.org/downloads/", 1195 | "license": [ 1196 | "MIT" 1197 | ], 1198 | "authors": [ 1199 | { 1200 | "name": "Nicolas Grekas", 1201 | "email": "p@tchwork.com" 1202 | }, 1203 | { 1204 | "name": "Symfony Community", 1205 | "homepage": "https://symfony.com/contributors" 1206 | } 1207 | ], 1208 | "description": "A set of abstractions extracted out of the Symfony components", 1209 | "homepage": "https://symfony.com", 1210 | "keywords": [ 1211 | "abstractions", 1212 | "contracts", 1213 | "decoupling", 1214 | "interfaces", 1215 | "interoperability", 1216 | "standards" 1217 | ], 1218 | "time": "2018-12-05T08:06:11+00:00" 1219 | }, 1220 | { 1221 | "name": "symfony/dependency-injection", 1222 | "version": "v4.2.5", 1223 | "source": { 1224 | "type": "git", 1225 | "url": "https://github.com/symfony/dependency-injection.git", 1226 | "reference": "1806e43ff6bff57398d33b326cd753a12d9f434f" 1227 | }, 1228 | "dist": { 1229 | "type": "zip", 1230 | "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/1806e43ff6bff57398d33b326cd753a12d9f434f", 1231 | "reference": "1806e43ff6bff57398d33b326cd753a12d9f434f", 1232 | "shasum": "" 1233 | }, 1234 | "require": { 1235 | "php": "^7.1.3", 1236 | "psr/container": "^1.0", 1237 | "symfony/contracts": "^1.0" 1238 | }, 1239 | "conflict": { 1240 | "symfony/config": "<4.2", 1241 | "symfony/finder": "<3.4", 1242 | "symfony/proxy-manager-bridge": "<3.4", 1243 | "symfony/yaml": "<3.4" 1244 | }, 1245 | "provide": { 1246 | "psr/container-implementation": "1.0", 1247 | "symfony/service-contracts-implementation": "1.0" 1248 | }, 1249 | "require-dev": { 1250 | "symfony/config": "~4.2", 1251 | "symfony/expression-language": "~3.4|~4.0", 1252 | "symfony/yaml": "~3.4|~4.0" 1253 | }, 1254 | "suggest": { 1255 | "symfony/config": "", 1256 | "symfony/expression-language": "For using expressions in service container configuration", 1257 | "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", 1258 | "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", 1259 | "symfony/yaml": "" 1260 | }, 1261 | "type": "library", 1262 | "extra": { 1263 | "branch-alias": { 1264 | "dev-master": "4.2-dev" 1265 | } 1266 | }, 1267 | "autoload": { 1268 | "psr-4": { 1269 | "Symfony\\Component\\DependencyInjection\\": "" 1270 | }, 1271 | "exclude-from-classmap": [ 1272 | "/Tests/" 1273 | ] 1274 | }, 1275 | "notification-url": "https://packagist.org/downloads/", 1276 | "license": [ 1277 | "MIT" 1278 | ], 1279 | "authors": [ 1280 | { 1281 | "name": "Fabien Potencier", 1282 | "email": "fabien@symfony.com" 1283 | }, 1284 | { 1285 | "name": "Symfony Community", 1286 | "homepage": "https://symfony.com/contributors" 1287 | } 1288 | ], 1289 | "description": "Symfony DependencyInjection Component", 1290 | "homepage": "https://symfony.com", 1291 | "time": "2019-03-30T15:58:42+00:00" 1292 | }, 1293 | { 1294 | "name": "symfony/filesystem", 1295 | "version": "v4.2.5", 1296 | "source": { 1297 | "type": "git", 1298 | "url": "https://github.com/symfony/filesystem.git", 1299 | "reference": "e16b9e471703b2c60b95f14d31c1239f68f11601" 1300 | }, 1301 | "dist": { 1302 | "type": "zip", 1303 | "url": "https://api.github.com/repos/symfony/filesystem/zipball/e16b9e471703b2c60b95f14d31c1239f68f11601", 1304 | "reference": "e16b9e471703b2c60b95f14d31c1239f68f11601", 1305 | "shasum": "" 1306 | }, 1307 | "require": { 1308 | "php": "^7.1.3", 1309 | "symfony/polyfill-ctype": "~1.8" 1310 | }, 1311 | "type": "library", 1312 | "extra": { 1313 | "branch-alias": { 1314 | "dev-master": "4.2-dev" 1315 | } 1316 | }, 1317 | "autoload": { 1318 | "psr-4": { 1319 | "Symfony\\Component\\Filesystem\\": "" 1320 | }, 1321 | "exclude-from-classmap": [ 1322 | "/Tests/" 1323 | ] 1324 | }, 1325 | "notification-url": "https://packagist.org/downloads/", 1326 | "license": [ 1327 | "MIT" 1328 | ], 1329 | "authors": [ 1330 | { 1331 | "name": "Fabien Potencier", 1332 | "email": "fabien@symfony.com" 1333 | }, 1334 | { 1335 | "name": "Symfony Community", 1336 | "homepage": "https://symfony.com/contributors" 1337 | } 1338 | ], 1339 | "description": "Symfony Filesystem Component", 1340 | "homepage": "https://symfony.com", 1341 | "time": "2019-02-07T11:40:08+00:00" 1342 | }, 1343 | { 1344 | "name": "symfony/polyfill-ctype", 1345 | "version": "v1.11.0", 1346 | "source": { 1347 | "type": "git", 1348 | "url": "https://github.com/symfony/polyfill-ctype.git", 1349 | "reference": "82ebae02209c21113908c229e9883c419720738a" 1350 | }, 1351 | "dist": { 1352 | "type": "zip", 1353 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", 1354 | "reference": "82ebae02209c21113908c229e9883c419720738a", 1355 | "shasum": "" 1356 | }, 1357 | "require": { 1358 | "php": ">=5.3.3" 1359 | }, 1360 | "suggest": { 1361 | "ext-ctype": "For best performance" 1362 | }, 1363 | "type": "library", 1364 | "extra": { 1365 | "branch-alias": { 1366 | "dev-master": "1.11-dev" 1367 | } 1368 | }, 1369 | "autoload": { 1370 | "psr-4": { 1371 | "Symfony\\Polyfill\\Ctype\\": "" 1372 | }, 1373 | "files": [ 1374 | "bootstrap.php" 1375 | ] 1376 | }, 1377 | "notification-url": "https://packagist.org/downloads/", 1378 | "license": [ 1379 | "MIT" 1380 | ], 1381 | "authors": [ 1382 | { 1383 | "name": "Symfony Community", 1384 | "homepage": "https://symfony.com/contributors" 1385 | }, 1386 | { 1387 | "name": "Gert de Pagter", 1388 | "email": "backendtea@gmail.com" 1389 | } 1390 | ], 1391 | "description": "Symfony polyfill for ctype functions", 1392 | "homepage": "https://symfony.com", 1393 | "keywords": [ 1394 | "compatibility", 1395 | "ctype", 1396 | "polyfill", 1397 | "portable" 1398 | ], 1399 | "time": "2019-02-06T07:57:58+00:00" 1400 | }, 1401 | { 1402 | "name": "symfony/polyfill-mbstring", 1403 | "version": "v1.11.0", 1404 | "source": { 1405 | "type": "git", 1406 | "url": "https://github.com/symfony/polyfill-mbstring.git", 1407 | "reference": "fe5e94c604826c35a32fa832f35bd036b6799609" 1408 | }, 1409 | "dist": { 1410 | "type": "zip", 1411 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609", 1412 | "reference": "fe5e94c604826c35a32fa832f35bd036b6799609", 1413 | "shasum": "" 1414 | }, 1415 | "require": { 1416 | "php": ">=5.3.3" 1417 | }, 1418 | "suggest": { 1419 | "ext-mbstring": "For best performance" 1420 | }, 1421 | "type": "library", 1422 | "extra": { 1423 | "branch-alias": { 1424 | "dev-master": "1.11-dev" 1425 | } 1426 | }, 1427 | "autoload": { 1428 | "psr-4": { 1429 | "Symfony\\Polyfill\\Mbstring\\": "" 1430 | }, 1431 | "files": [ 1432 | "bootstrap.php" 1433 | ] 1434 | }, 1435 | "notification-url": "https://packagist.org/downloads/", 1436 | "license": [ 1437 | "MIT" 1438 | ], 1439 | "authors": [ 1440 | { 1441 | "name": "Nicolas Grekas", 1442 | "email": "p@tchwork.com" 1443 | }, 1444 | { 1445 | "name": "Symfony Community", 1446 | "homepage": "https://symfony.com/contributors" 1447 | } 1448 | ], 1449 | "description": "Symfony polyfill for the Mbstring extension", 1450 | "homepage": "https://symfony.com", 1451 | "keywords": [ 1452 | "compatibility", 1453 | "mbstring", 1454 | "polyfill", 1455 | "portable", 1456 | "shim" 1457 | ], 1458 | "time": "2019-02-06T07:57:58+00:00" 1459 | }, 1460 | { 1461 | "name": "symfony/yaml", 1462 | "version": "v4.2.5", 1463 | "source": { 1464 | "type": "git", 1465 | "url": "https://github.com/symfony/yaml.git", 1466 | "reference": "6712daf03ee25b53abb14e7e8e0ede1a770efdb1" 1467 | }, 1468 | "dist": { 1469 | "type": "zip", 1470 | "url": "https://api.github.com/repos/symfony/yaml/zipball/6712daf03ee25b53abb14e7e8e0ede1a770efdb1", 1471 | "reference": "6712daf03ee25b53abb14e7e8e0ede1a770efdb1", 1472 | "shasum": "" 1473 | }, 1474 | "require": { 1475 | "php": "^7.1.3", 1476 | "symfony/polyfill-ctype": "~1.8" 1477 | }, 1478 | "conflict": { 1479 | "symfony/console": "<3.4" 1480 | }, 1481 | "require-dev": { 1482 | "symfony/console": "~3.4|~4.0" 1483 | }, 1484 | "suggest": { 1485 | "symfony/console": "For validating YAML files using the lint command" 1486 | }, 1487 | "type": "library", 1488 | "extra": { 1489 | "branch-alias": { 1490 | "dev-master": "4.2-dev" 1491 | } 1492 | }, 1493 | "autoload": { 1494 | "psr-4": { 1495 | "Symfony\\Component\\Yaml\\": "" 1496 | }, 1497 | "exclude-from-classmap": [ 1498 | "/Tests/" 1499 | ] 1500 | }, 1501 | "notification-url": "https://packagist.org/downloads/", 1502 | "license": [ 1503 | "MIT" 1504 | ], 1505 | "authors": [ 1506 | { 1507 | "name": "Fabien Potencier", 1508 | "email": "fabien@symfony.com" 1509 | }, 1510 | { 1511 | "name": "Symfony Community", 1512 | "homepage": "https://symfony.com/contributors" 1513 | } 1514 | ], 1515 | "description": "Symfony Yaml Component", 1516 | "homepage": "https://symfony.com", 1517 | "time": "2019-03-30T15:58:42+00:00" 1518 | } 1519 | ], 1520 | "packages-dev": [ 1521 | { 1522 | "name": "composer/semver", 1523 | "version": "1.5.0", 1524 | "source": { 1525 | "type": "git", 1526 | "url": "https://github.com/composer/semver.git", 1527 | "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e" 1528 | }, 1529 | "dist": { 1530 | "type": "zip", 1531 | "url": "https://api.github.com/repos/composer/semver/zipball/46d9139568ccb8d9e7cdd4539cab7347568a5e2e", 1532 | "reference": "46d9139568ccb8d9e7cdd4539cab7347568a5e2e", 1533 | "shasum": "" 1534 | }, 1535 | "require": { 1536 | "php": "^5.3.2 || ^7.0" 1537 | }, 1538 | "require-dev": { 1539 | "phpunit/phpunit": "^4.5 || ^5.0.5", 1540 | "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" 1541 | }, 1542 | "type": "library", 1543 | "extra": { 1544 | "branch-alias": { 1545 | "dev-master": "1.x-dev" 1546 | } 1547 | }, 1548 | "autoload": { 1549 | "psr-4": { 1550 | "Composer\\Semver\\": "src" 1551 | } 1552 | }, 1553 | "notification-url": "https://packagist.org/downloads/", 1554 | "license": [ 1555 | "MIT" 1556 | ], 1557 | "authors": [ 1558 | { 1559 | "name": "Nils Adermann", 1560 | "email": "naderman@naderman.de", 1561 | "homepage": "http://www.naderman.de" 1562 | }, 1563 | { 1564 | "name": "Jordi Boggiano", 1565 | "email": "j.boggiano@seld.be", 1566 | "homepage": "http://seld.be" 1567 | }, 1568 | { 1569 | "name": "Rob Bast", 1570 | "email": "rob.bast@gmail.com", 1571 | "homepage": "http://robbast.nl" 1572 | } 1573 | ], 1574 | "description": "Semver library that offers utilities, version constraint parsing and validation.", 1575 | "keywords": [ 1576 | "semantic", 1577 | "semver", 1578 | "validation", 1579 | "versioning" 1580 | ], 1581 | "time": "2019-03-19T17:25:45+00:00" 1582 | }, 1583 | { 1584 | "name": "composer/xdebug-handler", 1585 | "version": "1.3.2", 1586 | "source": { 1587 | "type": "git", 1588 | "url": "https://github.com/composer/xdebug-handler.git", 1589 | "reference": "d17708133b6c276d6e42ef887a877866b909d892" 1590 | }, 1591 | "dist": { 1592 | "type": "zip", 1593 | "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/d17708133b6c276d6e42ef887a877866b909d892", 1594 | "reference": "d17708133b6c276d6e42ef887a877866b909d892", 1595 | "shasum": "" 1596 | }, 1597 | "require": { 1598 | "php": "^5.3.2 || ^7.0", 1599 | "psr/log": "^1.0" 1600 | }, 1601 | "require-dev": { 1602 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" 1603 | }, 1604 | "type": "library", 1605 | "autoload": { 1606 | "psr-4": { 1607 | "Composer\\XdebugHandler\\": "src" 1608 | } 1609 | }, 1610 | "notification-url": "https://packagist.org/downloads/", 1611 | "license": [ 1612 | "MIT" 1613 | ], 1614 | "authors": [ 1615 | { 1616 | "name": "John Stevenson", 1617 | "email": "john-stevenson@blueyonder.co.uk" 1618 | } 1619 | ], 1620 | "description": "Restarts a process without xdebug.", 1621 | "keywords": [ 1622 | "Xdebug", 1623 | "performance" 1624 | ], 1625 | "time": "2019-01-28T20:25:53+00:00" 1626 | }, 1627 | { 1628 | "name": "doctrine/annotations", 1629 | "version": "v1.6.1", 1630 | "source": { 1631 | "type": "git", 1632 | "url": "https://github.com/doctrine/annotations.git", 1633 | "reference": "53120e0eb10355388d6ccbe462f1fea34ddadb24" 1634 | }, 1635 | "dist": { 1636 | "type": "zip", 1637 | "url": "https://api.github.com/repos/doctrine/annotations/zipball/53120e0eb10355388d6ccbe462f1fea34ddadb24", 1638 | "reference": "53120e0eb10355388d6ccbe462f1fea34ddadb24", 1639 | "shasum": "" 1640 | }, 1641 | "require": { 1642 | "doctrine/lexer": "1.*", 1643 | "php": "^7.1" 1644 | }, 1645 | "require-dev": { 1646 | "doctrine/cache": "1.*", 1647 | "phpunit/phpunit": "^6.4" 1648 | }, 1649 | "type": "library", 1650 | "extra": { 1651 | "branch-alias": { 1652 | "dev-master": "1.6.x-dev" 1653 | } 1654 | }, 1655 | "autoload": { 1656 | "psr-4": { 1657 | "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" 1658 | } 1659 | }, 1660 | "notification-url": "https://packagist.org/downloads/", 1661 | "license": [ 1662 | "MIT" 1663 | ], 1664 | "authors": [ 1665 | { 1666 | "name": "Roman Borschel", 1667 | "email": "roman@code-factory.org" 1668 | }, 1669 | { 1670 | "name": "Benjamin Eberlei", 1671 | "email": "kontakt@beberlei.de" 1672 | }, 1673 | { 1674 | "name": "Guilherme Blanco", 1675 | "email": "guilhermeblanco@gmail.com" 1676 | }, 1677 | { 1678 | "name": "Jonathan Wage", 1679 | "email": "jonwage@gmail.com" 1680 | }, 1681 | { 1682 | "name": "Johannes Schmitt", 1683 | "email": "schmittjoh@gmail.com" 1684 | } 1685 | ], 1686 | "description": "Docblock Annotations Parser", 1687 | "homepage": "http://www.doctrine-project.org", 1688 | "keywords": [ 1689 | "annotations", 1690 | "docblock", 1691 | "parser" 1692 | ], 1693 | "time": "2019-03-25T19:12:02+00:00" 1694 | }, 1695 | { 1696 | "name": "doctrine/lexer", 1697 | "version": "v1.0.1", 1698 | "source": { 1699 | "type": "git", 1700 | "url": "https://github.com/doctrine/lexer.git", 1701 | "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" 1702 | }, 1703 | "dist": { 1704 | "type": "zip", 1705 | "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", 1706 | "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", 1707 | "shasum": "" 1708 | }, 1709 | "require": { 1710 | "php": ">=5.3.2" 1711 | }, 1712 | "type": "library", 1713 | "extra": { 1714 | "branch-alias": { 1715 | "dev-master": "1.0.x-dev" 1716 | } 1717 | }, 1718 | "autoload": { 1719 | "psr-0": { 1720 | "Doctrine\\Common\\Lexer\\": "lib/" 1721 | } 1722 | }, 1723 | "notification-url": "https://packagist.org/downloads/", 1724 | "license": [ 1725 | "MIT" 1726 | ], 1727 | "authors": [ 1728 | { 1729 | "name": "Roman Borschel", 1730 | "email": "roman@code-factory.org" 1731 | }, 1732 | { 1733 | "name": "Guilherme Blanco", 1734 | "email": "guilhermeblanco@gmail.com" 1735 | }, 1736 | { 1737 | "name": "Johannes Schmitt", 1738 | "email": "schmittjoh@gmail.com" 1739 | } 1740 | ], 1741 | "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", 1742 | "homepage": "http://www.doctrine-project.org", 1743 | "keywords": [ 1744 | "lexer", 1745 | "parser" 1746 | ], 1747 | "time": "2014-09-09T13:34:57+00:00" 1748 | }, 1749 | { 1750 | "name": "friendsofphp/php-cs-fixer", 1751 | "version": "v2.14.2", 1752 | "source": { 1753 | "type": "git", 1754 | "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", 1755 | "reference": "ff401e58261ffc5934a58f795b3f95b355e276cb" 1756 | }, 1757 | "dist": { 1758 | "type": "zip", 1759 | "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/ff401e58261ffc5934a58f795b3f95b355e276cb", 1760 | "reference": "ff401e58261ffc5934a58f795b3f95b355e276cb", 1761 | "shasum": "" 1762 | }, 1763 | "require": { 1764 | "composer/semver": "^1.4", 1765 | "composer/xdebug-handler": "^1.2", 1766 | "doctrine/annotations": "^1.2", 1767 | "ext-json": "*", 1768 | "ext-tokenizer": "*", 1769 | "php": "^5.6 || ^7.0", 1770 | "php-cs-fixer/diff": "^1.3", 1771 | "symfony/console": "^3.4.17 || ^4.1.6", 1772 | "symfony/event-dispatcher": "^3.0 || ^4.0", 1773 | "symfony/filesystem": "^3.0 || ^4.0", 1774 | "symfony/finder": "^3.0 || ^4.0", 1775 | "symfony/options-resolver": "^3.0 || ^4.0", 1776 | "symfony/polyfill-php70": "^1.0", 1777 | "symfony/polyfill-php72": "^1.4", 1778 | "symfony/process": "^3.0 || ^4.0", 1779 | "symfony/stopwatch": "^3.0 || ^4.0" 1780 | }, 1781 | "require-dev": { 1782 | "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", 1783 | "justinrainbow/json-schema": "^5.0", 1784 | "keradus/cli-executor": "^1.2", 1785 | "mikey179/vfsstream": "^1.6", 1786 | "php-coveralls/php-coveralls": "^2.1", 1787 | "php-cs-fixer/accessible-object": "^1.0", 1788 | "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.0.1", 1789 | "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.0.1", 1790 | "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1", 1791 | "phpunitgoodpractices/traits": "^1.5.1", 1792 | "symfony/phpunit-bridge": "^4.0" 1793 | }, 1794 | "suggest": { 1795 | "ext-mbstring": "For handling non-UTF8 characters in cache signature.", 1796 | "php-cs-fixer/phpunit-constraint-isidenticalstring": "For IsIdenticalString constraint.", 1797 | "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "For XmlMatchesXsd constraint.", 1798 | "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." 1799 | }, 1800 | "bin": [ 1801 | "php-cs-fixer" 1802 | ], 1803 | "type": "application", 1804 | "autoload": { 1805 | "psr-4": { 1806 | "PhpCsFixer\\": "src/" 1807 | }, 1808 | "classmap": [ 1809 | "tests/Test/AbstractFixerTestCase.php", 1810 | "tests/Test/AbstractIntegrationCaseFactory.php", 1811 | "tests/Test/AbstractIntegrationTestCase.php", 1812 | "tests/Test/Assert/AssertTokensTrait.php", 1813 | "tests/Test/IntegrationCase.php", 1814 | "tests/Test/IntegrationCaseFactory.php", 1815 | "tests/Test/IntegrationCaseFactoryInterface.php", 1816 | "tests/Test/InternalIntegrationCaseFactory.php", 1817 | "tests/TestCase.php" 1818 | ] 1819 | }, 1820 | "notification-url": "https://packagist.org/downloads/", 1821 | "license": [ 1822 | "MIT" 1823 | ], 1824 | "authors": [ 1825 | { 1826 | "name": "Dariusz Rumiński", 1827 | "email": "dariusz.ruminski@gmail.com" 1828 | }, 1829 | { 1830 | "name": "Fabien Potencier", 1831 | "email": "fabien@symfony.com" 1832 | } 1833 | ], 1834 | "description": "A tool to automatically fix PHP code style", 1835 | "time": "2019-02-17T17:44:13+00:00" 1836 | }, 1837 | { 1838 | "name": "paragonie/random_compat", 1839 | "version": "v9.99.99", 1840 | "source": { 1841 | "type": "git", 1842 | "url": "https://github.com/paragonie/random_compat.git", 1843 | "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" 1844 | }, 1845 | "dist": { 1846 | "type": "zip", 1847 | "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", 1848 | "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", 1849 | "shasum": "" 1850 | }, 1851 | "require": { 1852 | "php": "^7" 1853 | }, 1854 | "require-dev": { 1855 | "phpunit/phpunit": "4.*|5.*", 1856 | "vimeo/psalm": "^1" 1857 | }, 1858 | "suggest": { 1859 | "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." 1860 | }, 1861 | "type": "library", 1862 | "notification-url": "https://packagist.org/downloads/", 1863 | "license": [ 1864 | "MIT" 1865 | ], 1866 | "authors": [ 1867 | { 1868 | "name": "Paragon Initiative Enterprises", 1869 | "email": "security@paragonie.com", 1870 | "homepage": "https://paragonie.com" 1871 | } 1872 | ], 1873 | "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", 1874 | "keywords": [ 1875 | "csprng", 1876 | "polyfill", 1877 | "pseudorandom", 1878 | "random" 1879 | ], 1880 | "time": "2018-07-02T15:55:56+00:00" 1881 | }, 1882 | { 1883 | "name": "php-cs-fixer/diff", 1884 | "version": "v1.3.0", 1885 | "source": { 1886 | "type": "git", 1887 | "url": "https://github.com/PHP-CS-Fixer/diff.git", 1888 | "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756" 1889 | }, 1890 | "dist": { 1891 | "type": "zip", 1892 | "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/78bb099e9c16361126c86ce82ec4405ebab8e756", 1893 | "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756", 1894 | "shasum": "" 1895 | }, 1896 | "require": { 1897 | "php": "^5.6 || ^7.0" 1898 | }, 1899 | "require-dev": { 1900 | "phpunit/phpunit": "^5.7.23 || ^6.4.3", 1901 | "symfony/process": "^3.3" 1902 | }, 1903 | "type": "library", 1904 | "autoload": { 1905 | "classmap": [ 1906 | "src/" 1907 | ] 1908 | }, 1909 | "notification-url": "https://packagist.org/downloads/", 1910 | "license": [ 1911 | "BSD-3-Clause" 1912 | ], 1913 | "authors": [ 1914 | { 1915 | "name": "Kore Nordmann", 1916 | "email": "mail@kore-nordmann.de" 1917 | }, 1918 | { 1919 | "name": "Sebastian Bergmann", 1920 | "email": "sebastian@phpunit.de" 1921 | }, 1922 | { 1923 | "name": "SpacePossum" 1924 | } 1925 | ], 1926 | "description": "sebastian/diff v2 backport support for PHP5.6", 1927 | "homepage": "https://github.com/PHP-CS-Fixer", 1928 | "keywords": [ 1929 | "diff" 1930 | ], 1931 | "time": "2018-02-15T16:58:55+00:00" 1932 | }, 1933 | { 1934 | "name": "symfony/event-dispatcher", 1935 | "version": "v4.2.5", 1936 | "source": { 1937 | "type": "git", 1938 | "url": "https://github.com/symfony/event-dispatcher.git", 1939 | "reference": "ca5af306fbc37f3cf597e91bc9cfa0c7d3f33544" 1940 | }, 1941 | "dist": { 1942 | "type": "zip", 1943 | "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ca5af306fbc37f3cf597e91bc9cfa0c7d3f33544", 1944 | "reference": "ca5af306fbc37f3cf597e91bc9cfa0c7d3f33544", 1945 | "shasum": "" 1946 | }, 1947 | "require": { 1948 | "php": "^7.1.3", 1949 | "symfony/contracts": "^1.0" 1950 | }, 1951 | "conflict": { 1952 | "symfony/dependency-injection": "<3.4" 1953 | }, 1954 | "require-dev": { 1955 | "psr/log": "~1.0", 1956 | "symfony/config": "~3.4|~4.0", 1957 | "symfony/dependency-injection": "~3.4|~4.0", 1958 | "symfony/expression-language": "~3.4|~4.0", 1959 | "symfony/stopwatch": "~3.4|~4.0" 1960 | }, 1961 | "suggest": { 1962 | "symfony/dependency-injection": "", 1963 | "symfony/http-kernel": "" 1964 | }, 1965 | "type": "library", 1966 | "extra": { 1967 | "branch-alias": { 1968 | "dev-master": "4.2-dev" 1969 | } 1970 | }, 1971 | "autoload": { 1972 | "psr-4": { 1973 | "Symfony\\Component\\EventDispatcher\\": "" 1974 | }, 1975 | "exclude-from-classmap": [ 1976 | "/Tests/" 1977 | ] 1978 | }, 1979 | "notification-url": "https://packagist.org/downloads/", 1980 | "license": [ 1981 | "MIT" 1982 | ], 1983 | "authors": [ 1984 | { 1985 | "name": "Fabien Potencier", 1986 | "email": "fabien@symfony.com" 1987 | }, 1988 | { 1989 | "name": "Symfony Community", 1990 | "homepage": "https://symfony.com/contributors" 1991 | } 1992 | ], 1993 | "description": "Symfony EventDispatcher Component", 1994 | "homepage": "https://symfony.com", 1995 | "time": "2019-03-30T15:58:42+00:00" 1996 | }, 1997 | { 1998 | "name": "symfony/finder", 1999 | "version": "v4.2.5", 2000 | "source": { 2001 | "type": "git", 2002 | "url": "https://github.com/symfony/finder.git", 2003 | "reference": "267b7002c1b70ea80db0833c3afe05f0fbde580a" 2004 | }, 2005 | "dist": { 2006 | "type": "zip", 2007 | "url": "https://api.github.com/repos/symfony/finder/zipball/267b7002c1b70ea80db0833c3afe05f0fbde580a", 2008 | "reference": "267b7002c1b70ea80db0833c3afe05f0fbde580a", 2009 | "shasum": "" 2010 | }, 2011 | "require": { 2012 | "php": "^7.1.3" 2013 | }, 2014 | "type": "library", 2015 | "extra": { 2016 | "branch-alias": { 2017 | "dev-master": "4.2-dev" 2018 | } 2019 | }, 2020 | "autoload": { 2021 | "psr-4": { 2022 | "Symfony\\Component\\Finder\\": "" 2023 | }, 2024 | "exclude-from-classmap": [ 2025 | "/Tests/" 2026 | ] 2027 | }, 2028 | "notification-url": "https://packagist.org/downloads/", 2029 | "license": [ 2030 | "MIT" 2031 | ], 2032 | "authors": [ 2033 | { 2034 | "name": "Fabien Potencier", 2035 | "email": "fabien@symfony.com" 2036 | }, 2037 | { 2038 | "name": "Symfony Community", 2039 | "homepage": "https://symfony.com/contributors" 2040 | } 2041 | ], 2042 | "description": "Symfony Finder Component", 2043 | "homepage": "https://symfony.com", 2044 | "time": "2019-02-23T15:42:05+00:00" 2045 | }, 2046 | { 2047 | "name": "symfony/options-resolver", 2048 | "version": "v4.2.5", 2049 | "source": { 2050 | "type": "git", 2051 | "url": "https://github.com/symfony/options-resolver.git", 2052 | "reference": "3896e5a7d06fd15fa4947694c8dcdd371ff147d1" 2053 | }, 2054 | "dist": { 2055 | "type": "zip", 2056 | "url": "https://api.github.com/repos/symfony/options-resolver/zipball/3896e5a7d06fd15fa4947694c8dcdd371ff147d1", 2057 | "reference": "3896e5a7d06fd15fa4947694c8dcdd371ff147d1", 2058 | "shasum": "" 2059 | }, 2060 | "require": { 2061 | "php": "^7.1.3" 2062 | }, 2063 | "type": "library", 2064 | "extra": { 2065 | "branch-alias": { 2066 | "dev-master": "4.2-dev" 2067 | } 2068 | }, 2069 | "autoload": { 2070 | "psr-4": { 2071 | "Symfony\\Component\\OptionsResolver\\": "" 2072 | }, 2073 | "exclude-from-classmap": [ 2074 | "/Tests/" 2075 | ] 2076 | }, 2077 | "notification-url": "https://packagist.org/downloads/", 2078 | "license": [ 2079 | "MIT" 2080 | ], 2081 | "authors": [ 2082 | { 2083 | "name": "Fabien Potencier", 2084 | "email": "fabien@symfony.com" 2085 | }, 2086 | { 2087 | "name": "Symfony Community", 2088 | "homepage": "https://symfony.com/contributors" 2089 | } 2090 | ], 2091 | "description": "Symfony OptionsResolver Component", 2092 | "homepage": "https://symfony.com", 2093 | "keywords": [ 2094 | "config", 2095 | "configuration", 2096 | "options" 2097 | ], 2098 | "time": "2019-02-23T15:17:42+00:00" 2099 | }, 2100 | { 2101 | "name": "symfony/polyfill-php70", 2102 | "version": "v1.11.0", 2103 | "source": { 2104 | "type": "git", 2105 | "url": "https://github.com/symfony/polyfill-php70.git", 2106 | "reference": "bc4858fb611bda58719124ca079baff854149c89" 2107 | }, 2108 | "dist": { 2109 | "type": "zip", 2110 | "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/bc4858fb611bda58719124ca079baff854149c89", 2111 | "reference": "bc4858fb611bda58719124ca079baff854149c89", 2112 | "shasum": "" 2113 | }, 2114 | "require": { 2115 | "paragonie/random_compat": "~1.0|~2.0|~9.99", 2116 | "php": ">=5.3.3" 2117 | }, 2118 | "type": "library", 2119 | "extra": { 2120 | "branch-alias": { 2121 | "dev-master": "1.11-dev" 2122 | } 2123 | }, 2124 | "autoload": { 2125 | "psr-4": { 2126 | "Symfony\\Polyfill\\Php70\\": "" 2127 | }, 2128 | "files": [ 2129 | "bootstrap.php" 2130 | ], 2131 | "classmap": [ 2132 | "Resources/stubs" 2133 | ] 2134 | }, 2135 | "notification-url": "https://packagist.org/downloads/", 2136 | "license": [ 2137 | "MIT" 2138 | ], 2139 | "authors": [ 2140 | { 2141 | "name": "Nicolas Grekas", 2142 | "email": "p@tchwork.com" 2143 | }, 2144 | { 2145 | "name": "Symfony Community", 2146 | "homepage": "https://symfony.com/contributors" 2147 | } 2148 | ], 2149 | "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", 2150 | "homepage": "https://symfony.com", 2151 | "keywords": [ 2152 | "compatibility", 2153 | "polyfill", 2154 | "portable", 2155 | "shim" 2156 | ], 2157 | "time": "2019-02-06T07:57:58+00:00" 2158 | }, 2159 | { 2160 | "name": "symfony/polyfill-php72", 2161 | "version": "v1.11.0", 2162 | "source": { 2163 | "type": "git", 2164 | "url": "https://github.com/symfony/polyfill-php72.git", 2165 | "reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c" 2166 | }, 2167 | "dist": { 2168 | "type": "zip", 2169 | "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/ab50dcf166d5f577978419edd37aa2bb8eabce0c", 2170 | "reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c", 2171 | "shasum": "" 2172 | }, 2173 | "require": { 2174 | "php": ">=5.3.3" 2175 | }, 2176 | "type": "library", 2177 | "extra": { 2178 | "branch-alias": { 2179 | "dev-master": "1.11-dev" 2180 | } 2181 | }, 2182 | "autoload": { 2183 | "psr-4": { 2184 | "Symfony\\Polyfill\\Php72\\": "" 2185 | }, 2186 | "files": [ 2187 | "bootstrap.php" 2188 | ] 2189 | }, 2190 | "notification-url": "https://packagist.org/downloads/", 2191 | "license": [ 2192 | "MIT" 2193 | ], 2194 | "authors": [ 2195 | { 2196 | "name": "Nicolas Grekas", 2197 | "email": "p@tchwork.com" 2198 | }, 2199 | { 2200 | "name": "Symfony Community", 2201 | "homepage": "https://symfony.com/contributors" 2202 | } 2203 | ], 2204 | "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", 2205 | "homepage": "https://symfony.com", 2206 | "keywords": [ 2207 | "compatibility", 2208 | "polyfill", 2209 | "portable", 2210 | "shim" 2211 | ], 2212 | "time": "2019-02-06T07:57:58+00:00" 2213 | }, 2214 | { 2215 | "name": "symfony/process", 2216 | "version": "v4.2.5", 2217 | "source": { 2218 | "type": "git", 2219 | "url": "https://github.com/symfony/process.git", 2220 | "reference": "1e6cbb41dadcaf29e0db034d6ad0d039a9df06e6" 2221 | }, 2222 | "dist": { 2223 | "type": "zip", 2224 | "url": "https://api.github.com/repos/symfony/process/zipball/1e6cbb41dadcaf29e0db034d6ad0d039a9df06e6", 2225 | "reference": "1e6cbb41dadcaf29e0db034d6ad0d039a9df06e6", 2226 | "shasum": "" 2227 | }, 2228 | "require": { 2229 | "php": "^7.1.3" 2230 | }, 2231 | "type": "library", 2232 | "extra": { 2233 | "branch-alias": { 2234 | "dev-master": "4.2-dev" 2235 | } 2236 | }, 2237 | "autoload": { 2238 | "psr-4": { 2239 | "Symfony\\Component\\Process\\": "" 2240 | }, 2241 | "exclude-from-classmap": [ 2242 | "/Tests/" 2243 | ] 2244 | }, 2245 | "notification-url": "https://packagist.org/downloads/", 2246 | "license": [ 2247 | "MIT" 2248 | ], 2249 | "authors": [ 2250 | { 2251 | "name": "Fabien Potencier", 2252 | "email": "fabien@symfony.com" 2253 | }, 2254 | { 2255 | "name": "Symfony Community", 2256 | "homepage": "https://symfony.com/contributors" 2257 | } 2258 | ], 2259 | "description": "Symfony Process Component", 2260 | "homepage": "https://symfony.com", 2261 | "time": "2019-03-10T20:07:02+00:00" 2262 | }, 2263 | { 2264 | "name": "symfony/stopwatch", 2265 | "version": "v4.2.5", 2266 | "source": { 2267 | "type": "git", 2268 | "url": "https://github.com/symfony/stopwatch.git", 2269 | "reference": "b1a5f646d56a3290230dbc8edf2a0d62cda23f67" 2270 | }, 2271 | "dist": { 2272 | "type": "zip", 2273 | "url": "https://api.github.com/repos/symfony/stopwatch/zipball/b1a5f646d56a3290230dbc8edf2a0d62cda23f67", 2274 | "reference": "b1a5f646d56a3290230dbc8edf2a0d62cda23f67", 2275 | "shasum": "" 2276 | }, 2277 | "require": { 2278 | "php": "^7.1.3", 2279 | "symfony/contracts": "^1.0" 2280 | }, 2281 | "type": "library", 2282 | "extra": { 2283 | "branch-alias": { 2284 | "dev-master": "4.2-dev" 2285 | } 2286 | }, 2287 | "autoload": { 2288 | "psr-4": { 2289 | "Symfony\\Component\\Stopwatch\\": "" 2290 | }, 2291 | "exclude-from-classmap": [ 2292 | "/Tests/" 2293 | ] 2294 | }, 2295 | "notification-url": "https://packagist.org/downloads/", 2296 | "license": [ 2297 | "MIT" 2298 | ], 2299 | "authors": [ 2300 | { 2301 | "name": "Fabien Potencier", 2302 | "email": "fabien@symfony.com" 2303 | }, 2304 | { 2305 | "name": "Symfony Community", 2306 | "homepage": "https://symfony.com/contributors" 2307 | } 2308 | ], 2309 | "description": "Symfony Stopwatch Component", 2310 | "homepage": "https://symfony.com", 2311 | "time": "2019-01-16T20:31:39+00:00" 2312 | } 2313 | ], 2314 | "aliases": [], 2315 | "minimum-stability": "stable", 2316 | "stability-flags": [], 2317 | "prefer-stable": false, 2318 | "prefer-lowest": false, 2319 | "platform": [], 2320 | "platform-dev": [] 2321 | } 2322 | -------------------------------------------------------------------------------- /config.dist.php: -------------------------------------------------------------------------------- 1 | './file.json', 5 | 'subject' => 'foo@bar.com', 6 | 'domain' => 'bar.com', 7 | 'users' => [ 8 | 'foo@bar.com' => 'organizer', 9 | ], 10 | 'blacklist' => [ 11 | 'Teamdrive Name' => [ 12 | 'bar@foo.com' 13 | ] 14 | ], 15 | 'whitelist' => [ 16 | 'Teamdrive Name' => [ 17 | 'bar@foo.com' 18 | ] 19 | ], 20 | 'teamDriveNameBegin' => '', 21 | 'thankFionera' => true, //Always add me (fionera) as Reader 22 | 'iam' => [ 23 | 'enabled' => false, 24 | 'projectId' => '' 25 | ] 26 | ]; 27 | -------------------------------------------------------------------------------- /config/services.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | _defaults: 3 | autowire: true 4 | autoconfigure: true 5 | public: true 6 | bind: 7 | $users: '%users%' 8 | $config: '%config%' 9 | 10 | _instanceof: 11 | Symfony\Component\Console\Command\Command: 12 | tags: ['console.command'] 13 | 14 | TeamdriveManager\: 15 | resource: '../src/*' 16 | exclude: '../src/{Struct}' -------------------------------------------------------------------------------- /src/Command/Assign/AssignWithGroupCommand.php: -------------------------------------------------------------------------------- 1 | googleGroupService = $googleGroupService; 65 | $this->googleDriveService = $googleDriveService; 66 | $this->config = $config; 67 | $this->googleIamService = $googleIamService; 68 | $this->users = $users; 69 | } 70 | 71 | protected function configure(): void 72 | { 73 | $this 74 | ->addOption('groupName', '-G', InputOption::VALUE_OPTIONAL, 'The group name for the service accounts') 75 | ->setDescription('Assign the Permissions to the Teamdrives with Google Groups'); 76 | 77 | } 78 | 79 | public function execute(InputInterface $input, OutputInterface $output) 80 | { 81 | $iamConfig = $this->config['iam']; 82 | if ($iamConfig['enabled'] === true) { 83 | 84 | $inputGroupName = $input->getOption('groupName'); 85 | if ($inputGroupName === null) { 86 | $inputGroupName = 'serviceaccountsgroup'; 87 | } 88 | 89 | self::$iamGroupMail = $this->getGroupAddress($inputGroupName); 90 | 91 | $iamGroupPromise = new Promise(function (callable $resolver, callable $canceler) { 92 | $this->googleGroupService->getGroupByEmail(self::$iamGroupMail)->then(function (Google_Service_Directory_Group $group) use ($resolver) { 93 | $resolver($group); 94 | }, function (\Exception $exception) use ($resolver) { 95 | if ($exception->getCode() === 404) { 96 | $this->googleGroupService->createGroup( 97 | 'Service Account Group', 98 | self::$iamGroupMail 99 | )->then(function (Google_Service_Directory_Group $group) use ($resolver) { 100 | $resolver($group); 101 | }); 102 | } 103 | }); 104 | }); 105 | 106 | $iamGroupPromise->then(function (Google_Service_Directory_Group $group) use ($iamConfig) { 107 | if (!is_array($iamConfig['projectId'])) { 108 | $iamConfig['projectId'] = [$iamConfig['projectId']]; 109 | } 110 | 111 | foreach ($iamConfig['projectId'] as $projectId) { 112 | $this->googleIamService->getServiceAccounts($projectId)->then(function (Google_Service_Iam_ListServiceAccountsResponse $serviceAccounts) use ($group) { 113 | $this->googleGroupService->getMembersForGroup($group)->then(function (Google_Service_Directory_Members $members) use ($serviceAccounts, $group) { 114 | $mails = []; 115 | /** @var Google_Service_Directory_Member $member */ 116 | foreach ($members as $member) { 117 | $mails[] = $member->getEmail(); 118 | } 119 | 120 | /** @var Google_Service_Iam_ServiceAccount $account */ 121 | foreach ($serviceAccounts->getAccounts() as $account) { 122 | if (!in_array($account->getEmail(), $mails, true)) { 123 | $this->googleGroupService->addMailToGroup($group, $account->getEmail()); 124 | } 125 | } 126 | }); 127 | }, function (Exception $exception) { 128 | var_dump($exception->getMessage()); 129 | echo "An Error Occurred\n"; 130 | }); 131 | } 132 | 133 | $this->globalGroups[] = $group; 134 | }); 135 | } 136 | 137 | // Request all Teamdrives and filter them 138 | $this->googleDriveService->getTeamDriveList(function (Google_Service_Drive_TeamDrive $teamDrive) { 139 | return strpos($teamDrive->getName(), $this->config['teamDriveNameBegin']) === 0; 140 | })->then(function (array $teamDriveArray) { // Check the Permissions for every Teamdrive with group and user check 141 | array_map([$this, 'checkPermissionsForTeamdrive'], $teamDriveArray); 142 | }); 143 | 144 | $this->googleDriveService->getTeamDriveList(function (Google_Service_Drive_TeamDrive $teamDrive) { 145 | return strpos($teamDrive->getName(), $this->config['teamDriveNameBegin']) === 0; 146 | })->then(function (array $teamDriveArray) { 147 | foreach ($teamDriveArray as $teamDrive) { 148 | $this->checkGroupPermissionsForTeamdrive($teamDrive); 149 | } 150 | }); 151 | } 152 | 153 | private function checkGroupPermissionsForTeamdrive(Google_Service_Drive_TeamDrive $teamDrive): void 154 | { 155 | $this->googleDriveService->getPermissionArray($teamDrive)->then(function (array $permissions) use ($teamDrive) { 156 | foreach ($this->getGroupsWithoutPermission($permissions, $teamDrive) as $group) { 157 | $this->checkPermissionForGroup($group, $teamDrive, null); 158 | } 159 | 160 | $permissionMails = []; 161 | /** @var $permissions Google_Service_Drive_Permission[] */ 162 | foreach ($permissions as $permission) { 163 | $permissionMails[strtolower($permission->getEmailAddress())] = $permission; 164 | } 165 | 166 | $globalGroupMails = []; 167 | foreach ($this->globalGroups as $group) { 168 | $globalGroupMails[strtolower($group->getEmail())] = $group; 169 | } 170 | 171 | foreach ($permissionMails as $permissionMail => $permission) { 172 | if (array_key_exists($permissionMail, $globalGroupMails)) { 173 | $this->checkPermissionForGroup($globalGroupMails[$permissionMail], $teamDrive, $permission, 'organizer'); 174 | unset($globalGroupMails[$permissionMail]); 175 | continue; 176 | } 177 | 178 | $group = $this->getGroupByEmail($permission->getEmailAddress()); 179 | $this->checkPermissionForGroup($group, $teamDrive, $permission); 180 | unset($permissionMails[$permissionMail]); 181 | } 182 | 183 | foreach ($globalGroupMails as $groupMail => $group) { 184 | $this->checkPermissionForGroup($group, $teamDrive, null, 'organizer'); 185 | } 186 | 187 | // /** @var $permissions Google_Service_Drive_Permission[] */ 188 | // foreach ($permissions as $permission) { 189 | // /** @var Google_Service_Directory_Group $group */ 190 | // foreach ($this->globalGroups as $group) { 191 | // if (strtolower($permission->getEmailAddress()) === strtolower($group->getEmail())) { 192 | // $this->checkPermissionForGroup($group, $teamDrive, $permission, 'organizer'); 193 | // continue 2; 194 | // } 195 | // } 196 | // 197 | // $group = $this->getGroupByEmail($permission->getEmailAddress()); 198 | // $this->checkPermissionForGroup($group, $teamDrive, $permission); 199 | // } 200 | }, function () { 201 | echo "An Error Occurred\n"; 202 | }); 203 | } 204 | 205 | /** 206 | * @param Google_Service_Drive_Permission[] $permissions 207 | * @param Google_Service_Drive_TeamDrive $teamDrive 208 | * 209 | * @return User[] 210 | */ 211 | private function getGroupsWithoutPermission(array $permissions, Google_Service_Drive_TeamDrive $teamDrive): array 212 | { 213 | $groupsWithoutPermission = []; 214 | /** @var Google_Service_Directory_Group $group */ 215 | foreach ($this->groupCache[$teamDrive->getName()] as $group) { 216 | foreach ($permissions as $permission) { 217 | if ($permission->getEmailAddress() === $group->getEmail()) { 218 | continue 2; 219 | } 220 | } 221 | 222 | $groupsWithoutPermission[] = $group; 223 | } 224 | 225 | return $groupsWithoutPermission; 226 | } 227 | 228 | private function checkPermissionForGroup(?Google_Service_Directory_Group $group, Google_Service_Drive_TeamDrive $teamDrive, ?Google_Service_Drive_Permission $permission, string $groupRole = null): void 229 | { 230 | if ($group !== null && $groupRole === null) { 231 | $groupRole = $this->getRoleForGroup($group); 232 | } 233 | 234 | if ($permission === null && $group !== null) { 235 | $this->googleDriveService->createPermissionForGroup($group, $groupRole, $teamDrive)->then(function () use ($group, $teamDrive) { 236 | echo 'Created Permission for Group ' . $group->getEmail() . ' on TeamDrive ' . $teamDrive->getName() . "\n"; 237 | }, function () use ($group, $teamDrive) { 238 | echo 'Error while creating Permission for Group ' . $group->getEmail() . ' on TeamDrive ' . $teamDrive->getName() . "\n"; 239 | }); 240 | 241 | return; 242 | } 243 | 244 | if ($permission !== null && $group !== null) { 245 | if ($permission->getRole() !== $groupRole) { 246 | $this->googleDriveService->updatePermissionForGroup($group, $groupRole, $teamDrive, $permission)->then(function () use ($group, $teamDrive) { 247 | echo 'Updated Permission for Group ' . $group->getEmail() . ' on TeamDrive ' . $teamDrive->getName() . "\n"; 248 | }, function () use ($group, $teamDrive) { 249 | echo 'Error while updating Permission for Group ' . $group->getEmail() . ' on TeamDrive ' . $teamDrive->getName() . "\n"; 250 | }); 251 | } 252 | 253 | return; 254 | } 255 | 256 | if ($permission !== null) { 257 | $this->googleDriveService->deletePermission($teamDrive, $permission)->then(function () use ($teamDrive, $permission) { 258 | echo 'Deleted Permission for Group ' . $permission->getEmailAddress() . ' on TeamDrive ' . $teamDrive->getName() . "\n"; 259 | }, function () use ($permission, $teamDrive) { 260 | echo 'Error while deleting Permission for Group ' . $permission->getEmailAddress() . ' on TeamDrive ' . $teamDrive->getName() . "\n"; 261 | }); 262 | } 263 | } 264 | 265 | private function getRoleForGroup(Google_Service_Directory_Group $givenGroup) 266 | { 267 | foreach ($this->groupCache as $teamdriveId => $groups) { 268 | /** 269 | * @var string 270 | * @var Google_Service_Directory_Group $group 271 | */ 272 | foreach ($groups as $role => $group) { 273 | if ($this->isMailEquals($givenGroup->getId(), $group->getId())) { 274 | return $role; 275 | } 276 | } 277 | } 278 | 279 | return null; 280 | } 281 | 282 | private function checkPermissionsForTeamdrive(Google_Service_Drive_TeamDrive $teamDrive): void 283 | { 284 | // Get all roles 285 | $roles = array_unique(array_map(function (User $user) { 286 | return $user->role; 287 | }, $this->users)); 288 | 289 | // For every role 290 | foreach ($roles as $role) { 291 | // Load Group for the current Teamdrive and the role 292 | $this->loadGroupForTeamdriveAndRole($teamDrive, $role)->then(function (Google_Service_Directory_Group $group) { 293 | // Get Members for the group 294 | $this->googleGroupService->getMembersForGroup($group)->then(function (Google_Service_Directory_Members $members) use ($group) { 295 | $this->memberCache[$group->getId()] = $members; 296 | }); 297 | }); 298 | } 299 | 300 | /** 301 | * for every role and group for this teamdrive 302 | * 303 | * @var string 304 | * @var Google_Service_Directory_Group $group 305 | */ 306 | foreach ($this->groupCache[$teamDrive->getName()] as $role => $group) { 307 | // Go over every User 308 | foreach ($this->users as $user) { 309 | // and check the Permission for him 310 | $this->checkPermissionForTeamdriveUser($teamDrive, $group, $role, $user); 311 | } 312 | 313 | /** @var Google_Service_Directory_Member $member */ 314 | // For every member in group 315 | foreach ($this->getMembersForGroup($group) as $member) { 316 | // if not in the allowed list, remove 317 | if (!\in_array(strtolower($member->getEmail()), $this->getUserEmails($this->getUsersForTeamdrive($teamDrive)), true)) { 318 | $this->googleGroupService->removeMemberFromGroup($group, $member); 319 | } 320 | } 321 | } 322 | } 323 | 324 | private function getUsersForTeamdrive(Google_Service_Drive_TeamDrive $teamDrive) 325 | { 326 | $users = []; 327 | 328 | foreach ($this->users as $user) { 329 | if (count($user->whitelist) !== 0) { 330 | if (in_array($teamDrive->getName(), $user->whitelist, true)) { 331 | $users[] = $user; 332 | } 333 | } elseif (!$this->isUserExcludedFromTeamDrive($user, $teamDrive->getName())) { 334 | $users[] = $user; 335 | } 336 | } 337 | 338 | return $users; 339 | } 340 | 341 | private function isUserExcludedFromTeamDrive(User $user, string $teamDriveName): bool 342 | { 343 | return \in_array($teamDriveName, $user->excluded, true); 344 | } 345 | 346 | private function getUserEmails(array $users = null) 347 | { 348 | return array_map(function (User $user) { 349 | return strtolower($user->mail); 350 | }, $users ?? $this->users); 351 | } 352 | 353 | private function getGroupByEmail(string $email) 354 | { 355 | foreach ($this->groupCache as $teamdriveId => $groups) { 356 | /** 357 | * @var string 358 | * @var Google_Service_Directory_Group $group 359 | */ 360 | foreach ($groups as $role => $group) { 361 | if ($this->isMailEquals($email, $group->getEmail())) { 362 | return $group; 363 | } 364 | } 365 | } 366 | 367 | return null; 368 | } 369 | 370 | private function getMembersForGroup(Google_Service_Directory_Group $group) 371 | { 372 | return $this->memberCache[$group->getId()] ?? []; 373 | } 374 | 375 | private function checkPermissionForTeamdriveUser(Google_Service_Drive_TeamDrive $teamDrive, Google_Service_Directory_Group $group, string $groupRole, User $user) 376 | { 377 | /** @var Google_Service_Directory_Member $member */ 378 | foreach ($this->getMembersForGroup($group) as $member) { 379 | /* @noinspection NotOptimalIfConditionsInspection */ 380 | if ($this->isMailEquals($member->getEmail(), $user->mail) && $groupRole !== $user->role) { 381 | $this->googleGroupService->removeMemberFromGroup($group, $member); 382 | break; 383 | } 384 | 385 | if ($groupRole === $user->role && $this->isMailEquals($member->getEmail(), $user->mail)) { 386 | return; 387 | } 388 | } 389 | unset($member); 390 | 391 | if ($groupRole === $user->role && !$this->isUserExcludedFromTeamDrive($user, $teamDrive->getName())) { 392 | $this->googleGroupService->addMailToGroup($group, $user->mail); 393 | } 394 | } 395 | 396 | private function loadGroupForTeamdriveAndRole(Google_Service_Drive_TeamDrive $teamDrive, string $role): PromiseInterface 397 | { 398 | return new Promise(function (callable $resolver, callable $canceler) use ($teamDrive, $role) { 399 | if (isset($this->groupCache[$teamDrive->getName()][$role])) { 400 | $resolver($this->groupCache[$teamDrive->getName()][$role]); 401 | 402 | return; 403 | } 404 | 405 | $this->googleGroupService->getGroupByEmail($this->getGroupAddressForTeamdrive($teamDrive, $role))->then(function (Google_Service_Directory_Group $group) use ($teamDrive, $role, $resolver) { 406 | $this->groupCache[$teamDrive->getName()][$role] = $group; 407 | 408 | $resolver($group); 409 | }, function (\Exception $exception) use ($teamDrive, $role, $resolver) { 410 | if ($exception->getCode() === 404) { 411 | $this->googleGroupService->createGroup( 412 | $this->getGroupNameForTeamdrive($teamDrive, $role), 413 | $this->getGroupAddressForTeamdrive($teamDrive, $role) 414 | )->then(function (Google_Service_Directory_Group $group) use ($teamDrive, $role, $resolver) { 415 | $this->groupCache[$teamDrive->getName()][$role] = $group; 416 | 417 | $resolver($group); 418 | }); 419 | } 420 | }); 421 | }); 422 | } 423 | 424 | private function isMailEquals(string $mailA, string $mailB): bool 425 | { 426 | return strtolower($mailA) === strtolower($mailB); 427 | } 428 | 429 | private function getGroupNameForTeamdrive(Google_Service_Drive_TeamDrive $teamDrive, string $role) 430 | { 431 | return ucfirst($role) . ' | ' . $teamDrive->getName(); 432 | } 433 | 434 | private function getGroupAddressForTeamdrive(Google_Service_Drive_TeamDrive $teamDrive, string $role) 435 | { 436 | return $this->getGroupAddress(hash('sha256', $teamDrive->getId() . '_' . $role)); 437 | } 438 | 439 | private function getGroupAddress(string $address) 440 | { 441 | return $address . '@' . $this->config['domain']; 442 | } 443 | } 444 | -------------------------------------------------------------------------------- /src/Command/Group/ListGroupMembers.php: -------------------------------------------------------------------------------- 1 | setDescription('Get all members for a Group'); 23 | } 24 | 25 | public function __construct(GoogleGroupService $googleGroupService) 26 | { 27 | parent::__construct(); 28 | $this->googleGroupService = $googleGroupService; 29 | } 30 | 31 | public function run(InputInterface $input, OutputInterface $output) 32 | { 33 | $io = new SymfonyStyle($input, $output); 34 | 35 | $address = $io->ask('Group Address'); 36 | 37 | $this->googleGroupService->getGroupByEmail($address)->then(function (Google_Service_Directory_Group $group) use ($output) { 38 | $this->googleGroupService->getMembersForGroup($group)->then(function (array $members) use ($output) { 39 | /** @var \Google_Service_Directory_Member $member */ 40 | foreach ($members as $member) { 41 | if ($member->getEmail() === null) { 42 | continue; 43 | } 44 | 45 | $output->writeln($member->getEmail()); 46 | } 47 | }); 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Command/Iam/CreateServiceAccountCommand.php: -------------------------------------------------------------------------------- 1 | config = $config; 32 | $this->iamService = $iamService; 33 | } 34 | 35 | protected function configure() 36 | { 37 | $this 38 | ->addOption('name', '-N', InputOption::VALUE_REQUIRED, 'The name for the Service Account') 39 | ->addOption('fileName', '-f', InputOption::VALUE_REQUIRED, 'The filename for the Key') 40 | ->addOption('amount', '-a', InputOption::VALUE_OPTIONAL, 'The amount of accounts to create', 1) 41 | ->addOption('projectId', '-p', InputOption::VALUE_OPTIONAL, 'The Project ID') 42 | ->setDescription('Create one or multiple Service Accounts') 43 | ; 44 | } 45 | 46 | protected function execute(InputInterface $input, OutputInterface $output) 47 | { 48 | $io = new SymfonyStyle($input, $output); 49 | 50 | $iam = $this->config['iam']; 51 | 52 | if ($iam['enabled'] !== true) { 53 | $output->writeln('IAM is disabled. Please enable it for use.'); 54 | 55 | return; 56 | } 57 | 58 | $inputName = $input->getOption('name'); 59 | if ($inputName === null) { 60 | $inputName = $io->ask('Serviceaccount Name'); 61 | } 62 | 63 | $amount = $input->getOption('amount'); 64 | 65 | if (is_array($iam['projectId'])) { 66 | $iam['projectId'] = $iam['projectId'][0]; 67 | } 68 | 69 | if ($input->getOption('projectId') !== null) { 70 | $iam['projectId'] = $input->getOption('projectId'); 71 | } 72 | 73 | for ($i = 1; $i < $amount + 1; ++$i) { 74 | $name = str_replace('INDEX', $i, $inputName); 75 | 76 | $this->iamService->createServiceAccount($iam['projectId'], $name)->then(function (Google_Service_Iam_ServiceAccount $serviceAccount) use ($output, $input, $i) { 77 | $output->writeln('Successfully created, creating Key'); 78 | 79 | $this->iamService->createServiceAccountKey($serviceAccount)->then(function (Google_Service_Iam_ServiceAccountKey $serviceAccountKey) use ($output, $input, $serviceAccount, $i) { 80 | $fileName = $input->getOption('fileName'); 81 | 82 | if ($fileName === null) { 83 | $fileName = $serviceAccount->getProjectId() . '-' . strtolower(str_replace(' ', '-', $serviceAccount->displayName)) . '.json'; 84 | } 85 | 86 | if (!is_dir('Files') && !mkdir('Files') && !is_dir('Files')) { 87 | throw new \RuntimeException(sprintf('Directory "%s" was not created', 'Files')); 88 | } 89 | 90 | file_put_contents('Files/' . $fileName, base64_decode($serviceAccountKey->getPrivateKeyData())); 91 | 92 | $output->writeln('Successfully created, saved to ' . $fileName); 93 | }, function (Exception $exception) { 94 | var_dump($exception->getMessage()); 95 | echo "An Error Occurred\n"; 96 | }); 97 | }, function (Exception $exception) { 98 | var_dump($exception->getMessage()); 99 | echo "An Error Occurred\n"; 100 | }); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Command/Iam/ListServiceAccountsCommand.php: -------------------------------------------------------------------------------- 1 | setDescription('List all Service Accounts'); 29 | } 30 | 31 | public function __construct(array $config, GoogleIamService $iamService) 32 | { 33 | parent::__construct(); 34 | $this->config = $config; 35 | $this->iamService = $iamService; 36 | } 37 | 38 | protected function execute(InputInterface $input, OutputInterface $output) 39 | { 40 | $iam = $this->config['iam']; 41 | 42 | if ($iam['enabled'] !== true) { 43 | $output->writeln('IAM is disabled. Please enable it for use.'); 44 | 45 | return; 46 | } 47 | 48 | if (!is_array($iam['projectId'])) { 49 | $iam['projectId'] = [$iam['projectId']]; 50 | } 51 | 52 | foreach ($iam['projectId'] as $projectId) { 53 | $this->iamService->getServiceAccounts($projectId)->then(function (Google_Service_Iam_ListServiceAccountsResponse $serviceAccounts) use ($output) { 54 | /** @var Google_Service_Iam_ServiceAccount $account */ 55 | foreach ($serviceAccounts->getAccounts() as $account) { 56 | $output->writeln($account->getDisplayName()); 57 | } 58 | }, function (Exception $exception) { 59 | var_dump($exception->getMessage()); 60 | echo "An Error Occurred\n"; 61 | }); 62 | } 63 | 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Command/Rclone/RcloneConfigCommand.php: -------------------------------------------------------------------------------- 1 | googleDriveService = $googleDriveServiceService; 35 | $this->config = $config; 36 | $this->rcloneConfigService = $rcloneConfigService; 37 | } 38 | 39 | 40 | protected function configure() 41 | { 42 | $this->setDescription('Create a Rclone Config file for all Teamdrives your User has access to'); 43 | $this 44 | ->addOption('sa-folder-path','-P', InputOption::VALUE_REQUIRED, 'The service account folder path.'); 45 | } 46 | 47 | public function execute(InputInterface $input, OutputInterface $output) 48 | { 49 | $io = new SymfonyStyle($input, $output); 50 | 51 | $path = $input->getOption('sa-folder-path'); 52 | 53 | $this->googleDriveService->getTeamDriveList(function (Google_Service_Drive_TeamDrive $teamDrive) { 54 | return true; 55 | })->then(function (array $teamDriveArray) use ($path) { 56 | $configFileString = $this->rcloneConfigService->createRcloneEntriesForTeamDriveList($teamDriveArray, '', $path); 57 | file_put_contents('rclone.conf', $configFileString); 58 | }); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Command/Teamdrive/CreateTeamdriveCommand.php: -------------------------------------------------------------------------------- 1 | googleDriveService = $googleDriveServiceService; 29 | $this->config = $config; 30 | } 31 | 32 | 33 | public function execute(InputInterface $input, OutputInterface $output) 34 | { 35 | $io = new SymfonyStyle($input, $output); 36 | 37 | $name = $input->getOption('name'); 38 | if ($name === null) { 39 | $name = $io->ask('Teamdrive Name'); 40 | } 41 | 42 | if ($input->getOption('prefix')) { 43 | $name = $this->config['teamDriveNameBegin'] . $name; 44 | } 45 | 46 | $this->googleDriveService->createTeamDrive($name)->then(function () use ($name) { 47 | }); 48 | } 49 | 50 | protected function configure() 51 | { 52 | $this 53 | ->addOption('name', '-N', InputOption::VALUE_REQUIRED, 'The name for the Teamdrive') 54 | ->addOption('prefix', '-p', InputOption::VALUE_NONE, 'Should the prefix be added') 55 | ->setDescription('Create a Teamdrive with a given Name') 56 | ; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Command/Teamdrive/ListTeamdrivesCommand.php: -------------------------------------------------------------------------------- 1 | setDescription('List all Teamdrives that you have access to'); 29 | } 30 | 31 | public function __construct(GoogleDriveService $googleDriveServiceService) 32 | { 33 | parent::__construct(); 34 | $this->googleDriveService = $googleDriveServiceService; 35 | } 36 | 37 | public function execute(InputInterface $input, OutputInterface $output) 38 | { 39 | $this->googleDriveService->getTeamDriveList(function (Google_Service_Drive_TeamDrive $teamDrive) { 40 | return true; //Return true since we want all Teamdrives 41 | })->then(function (array $teamDrives) { 42 | array_map([$this, 'getPermissionsForTeamDrive'], $teamDrives); 43 | }); 44 | 45 | /** 46 | * @var Google_Service_Drive_TeamDrive 47 | * @var Google_Service_Drive_Permission[] $permissions 48 | */ 49 | foreach ($this->teamDrives as $teamDriveName => $permissions) { 50 | $output->writeln('Permissions for TeamDrive "' . $teamDriveName . '":'); 51 | 52 | /** @var Google_Service_Drive_Permission $permission */ 53 | foreach ($permissions as $permission) { 54 | $output->writeln("\t" . $permission->getRole() . ' - ' . $permission->getEmailAddress()); 55 | } 56 | } 57 | } 58 | 59 | private function getPermissionsForTeamDrive(Google_Service_Drive_TeamDrive $teamDrive) 60 | { 61 | $this->teamDrives[$teamDrive->getName()] = []; 62 | $this->googleDriveService->getPermissionArray($teamDrive)->then(function (array $permissions) use ($teamDrive) { 63 | array_map(function (Google_Service_Drive_Permission $permission) use ($teamDrive) { 64 | $this->teamDrives[$teamDrive->getName()][] = $permission; 65 | }, $permissions); 66 | }); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Service/GoogleDriveService.php: -------------------------------------------------------------------------------- 1 | driveService = $drive_service; 36 | $this->requestQueue = $requestQueue; 37 | } 38 | 39 | public function updatePermissionForUser(User $user, Google_Service_Drive_TeamDrive $teamDrive, Google_Service_Drive_Permission $permission): PromiseInterface 40 | { 41 | echo 'Updating ' . $user->mail . ' for Teamdrive ' . $teamDrive->getName() . "\n"; 42 | 43 | $newPermission = new Google_Service_Drive_Permission(); 44 | $newPermission->setRole($user->role); 45 | 46 | /** @var \GuzzleHttp\Psr7\Request $request */ 47 | $request = $this->driveService->permissions->update($teamDrive->getId(), $permission->getId(), $newPermission, [ 48 | 'supportsTeamDrives' => true, 49 | ] 50 | ); 51 | 52 | return $this->requestQueue->queueRequest($request); 53 | } 54 | 55 | public function updatePermissionForGroup(Google_Service_Directory_Group $group, string $role, Google_Service_Drive_TeamDrive $teamDrive, Google_Service_Drive_Permission $permission): PromiseInterface 56 | { 57 | echo 'Updating ' . $group->getEmail() . ' for Teamdrive ' . $teamDrive->getName() . "\n"; 58 | 59 | $newPermission = new Google_Service_Drive_Permission(); 60 | $newPermission->setRole($role); 61 | 62 | /** @var \GuzzleHttp\Psr7\Request $request */ 63 | $request = $this->driveService->permissions->update($teamDrive->getId(), $permission->getId(), $newPermission, [ 64 | 'supportsTeamDrives' => true, 65 | ] 66 | ); 67 | 68 | return $this->requestQueue->queueRequest($request); 69 | } 70 | 71 | public function createPermissionForUser(User $user, Google_Service_Drive_TeamDrive $teamDrive): PromiseInterface 72 | { 73 | echo 'Creating ' . $user->mail . ' for Teamdrive ' . $teamDrive->getName() . "\n"; 74 | 75 | /** @var \GuzzleHttp\Psr7\Request $request */ 76 | $request = $this->driveService->permissions->create( 77 | $teamDrive->getId(), 78 | new Google_Service_Drive_Permission([ 79 | 'type' => 'user', 80 | 'role' => $user->role, 81 | 'emailAddress' => $user->mail 82 | ]), [ 83 | 'supportsTeamDrives' => true, 84 | 'sendNotificationEmail' => false, 85 | ] 86 | ); 87 | 88 | return $this->requestQueue->queueRequest($request); 89 | } 90 | 91 | public function createPermissionForGroup(Google_Service_Directory_Group $group, string $role, Google_Service_Drive_TeamDrive $teamDrive): PromiseInterface 92 | { 93 | echo 'Creating ' . $group->getEmail() . ' for Teamdrive ' . $teamDrive->getName() . "\n"; 94 | 95 | /** @var \GuzzleHttp\Psr7\Request $request */ 96 | $request = $this->driveService->permissions->create( 97 | $teamDrive->getId(), 98 | new Google_Service_Drive_Permission([ 99 | 'type' => 'group', 100 | 'role' => $role, 101 | 'emailAddress' => $group->getEmail() 102 | ]), [ 103 | 'supportsTeamDrives' => true, 104 | 'sendNotificationEmail' => false, 105 | ] 106 | ); 107 | 108 | return $this->requestQueue->queueRequest($request); 109 | } 110 | 111 | public function deletePermission(Google_Service_Drive_TeamDrive $teamDrive, Google_Service_Drive_Permission $permission): PromiseInterface 112 | { 113 | echo 'Deleting ' . $permission->getEmailAddress() . ' for Teamdrive ' . $teamDrive->getName() . "\n"; 114 | 115 | /** @var \GuzzleHttp\Psr7\Request $request */ 116 | $request = $this->driveService->permissions->delete($teamDrive->getId(), $permission->getId(), [ 117 | 'supportsTeamDrives' => true, 118 | ]); 119 | 120 | return $this->requestQueue->queueRequest($request); 121 | } 122 | 123 | public function getPermission(Google_Service_Drive_TeamDrive $teamDrive, string $id): PromiseInterface 124 | { 125 | echo 'Getting Permission ' . $id . ' for Teamdrive ' . $teamDrive->getName() . "\n"; 126 | 127 | /** @var \GuzzleHttp\Psr7\Request $request */ 128 | $request = $this->driveService->permissions->get($teamDrive->getId(), $id, [ 129 | 'supportsTeamDrives' => true, 130 | 'fields' => 'kind,id,emailAddress,domain,role,allowFileDiscovery,displayName,photoLink,expirationTime,teamDrivePermissionDetails,deleted' 131 | ]); 132 | 133 | return $this->requestQueue->queueRequest($request); 134 | } 135 | 136 | public function getPermissionList(Google_Service_Drive_TeamDrive $teamDrive): PromiseInterface 137 | { 138 | echo 'Getting PermissionList for ' . $teamDrive->getName() . "\n"; 139 | 140 | /** @var \GuzzleHttp\Psr7\Request $request */ 141 | $request = $this->driveService->permissions->listPermissions($teamDrive->getId(), [ 142 | 'supportsTeamDrives' => true, 143 | ]); 144 | 145 | return $this->requestQueue->queueRequest($request); 146 | } 147 | 148 | public function getPermissionArray(Google_Service_Drive_TeamDrive $teamDrive): PromiseInterface 149 | { 150 | echo 'Getting Permissions for ' . $teamDrive->getName() . "\n"; 151 | 152 | return $this->getPermissionList($teamDrive)->then(function (Google_Service_Drive_PermissionList $permissionList) use ($teamDrive) { 153 | $permissionRequestPromises = []; 154 | 155 | /** @var Google_Service_Drive_Permission $permission */ 156 | foreach ($permissionList as $permission) { 157 | $permissionRequestPromises[] = $this->getPermission($teamDrive, $permission->getId()); 158 | } 159 | 160 | return \GuzzleHttp\Promise\all($permissionRequestPromises); 161 | }); 162 | } 163 | 164 | public function getTeamDriveList(callable $filter, int $pageSize = 100, string $pageToken = '', $filteredDriveList = []): PromiseInterface 165 | { 166 | echo 'Getting Filtered TeamDrive List' . "\n"; 167 | 168 | /** @var \GuzzleHttp\Psr7\Request $request */ 169 | $request = $this->driveService->teamdrives->listTeamdrives([ 170 | 'pageSize' => $pageSize, 171 | 'pageToken' => $pageToken, 172 | ]); 173 | 174 | return $this->requestQueue->queueRequest($request)->then(function (Google_Service_Drive_TeamDriveList $teamDriveList) use ($filter, $filteredDriveList) { 175 | foreach ($teamDriveList as $teamDrive) { 176 | if ($filter($teamDrive) === true) { 177 | $filteredDriveList[] = $teamDrive; 178 | } 179 | } 180 | 181 | /** @var string $nextPageToken */ 182 | $nextPageToken = $teamDriveList->getNextPageToken(); 183 | 184 | if ($nextPageToken !== null && $nextPageToken !== '') { 185 | return $this->getTeamDriveList($filter, 100, $nextPageToken, $filteredDriveList); 186 | } 187 | 188 | return $filteredDriveList; 189 | }); 190 | } 191 | 192 | public function createTeamDrive(string $teamDriveName): PromiseInterface 193 | { 194 | echo 'Creating Teamdrive ' . $teamDriveName . "\n"; 195 | 196 | $teamDrive = new Google_Service_Drive_TeamDrive(); 197 | $teamDrive->setName($teamDriveName); 198 | try { 199 | $requestId = random_int(1, 10000000); 200 | } catch (Exception $e) { 201 | echo 'Cant get random int for id'; 202 | die(); 203 | } 204 | 205 | /** @var \GuzzleHttp\Psr7\Request $request */ 206 | $request = $this->driveService->teamdrives->create($requestId, $teamDrive); 207 | 208 | return $this->requestQueue->queueRequest($request); 209 | } 210 | 211 | public function getFileInformation(string $fileID): PromiseInterface 212 | { 213 | /** @var \GuzzleHttp\Psr7\Request $request */ 214 | $request = $this->driveService->files->get($fileID, [ 215 | 'supportsTeamDrives' => true 216 | ]); 217 | 218 | return $this->requestQueue->queueRequest($request); 219 | } 220 | 221 | public function downloadFile(string $fileID): PromiseInterface 222 | { 223 | /** @var \GuzzleHttp\Psr7\Request $request */ 224 | $request = $this->driveService->files->get($fileID, [ 225 | 'supportsTeamDrives' => true, 226 | 'alt' => 'media', 227 | ]); 228 | 229 | return $this->requestQueue->queueStreamRequest($request); 230 | } 231 | 232 | public function createFileCopy(string $fileID): PromiseInterface 233 | { 234 | /** @var \GuzzleHttp\Psr7\Request $request */ 235 | $request = $this->driveService->files->copy( 236 | $fileID, 237 | new Google_Service_Drive_DriveFile(), 238 | [ 239 | 'supportsTeamDrives' => true, 240 | ] 241 | ); 242 | 243 | return $this->requestQueue->queueRequest($request); 244 | } 245 | 246 | public function deleteFile(string $fileID): PromiseInterface 247 | { 248 | /** @var \GuzzleHttp\Psr7\Request $request */ 249 | $request = $this->driveService->files->delete( 250 | $fileID, 251 | [ 252 | 'supportsTeamDrives' => true, 253 | ] 254 | ); 255 | 256 | return $this->requestQueue->queueRequest($request); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/Service/GoogleGroupService.php: -------------------------------------------------------------------------------- 1 | directoryService = $directoryService; 33 | $this->requestQueue = $requestQueue; 34 | } 35 | 36 | public function getGroupByEmail(string $email): PromiseInterface 37 | { 38 | echo 'Retrieving Group for Address ' . $email . "\n"; 39 | 40 | /** @var \GuzzleHttp\Psr7\Request $request */ 41 | $request = $this->directoryService->groups->get($email); 42 | 43 | return $this->requestQueue->queueRequest($request); 44 | } 45 | 46 | public function createGroup(string $name, string $email, string $description = 'Created by TeamdriveManager'): PromiseInterface 47 | { 48 | echo 'Creating Group ' . $name . ' with Address ' . $email . "\n"; 49 | 50 | $group = new Google_Service_Directory_Group(); 51 | $group->setName($name); 52 | $group->setEmail($email); 53 | $group->setDescription($description); 54 | 55 | /** @var \GuzzleHttp\Psr7\Request $request */ 56 | $request = $this->directoryService->groups->insert($group); 57 | 58 | return $this->requestQueue->queueRequest($request); 59 | } 60 | 61 | public function addMailToGroup(Google_Service_Directory_Group $group, string $mail): PromiseInterface 62 | { 63 | echo 'Adding User ' . $mail . ' to Group ' . $group->getName() . ' with Address ' . $group->getEmail() . "\n"; 64 | 65 | $member = new Google_Service_Directory_Member(); 66 | $member->setEmail($mail); 67 | $member->setRole('MEMBER'); 68 | $member->setDeliverySettings('NONE'); 69 | 70 | /** @var \GuzzleHttp\Psr7\Request $request */ 71 | $request = $this->directoryService->members->insert($group->getId(), $member); 72 | 73 | return $this->requestQueue->queueRequest($request); 74 | } 75 | 76 | public function removeMemberFromGroup(Google_Service_Directory_Group $group, Google_Service_Directory_Member $member): PromiseInterface 77 | { 78 | echo 'Removing User ' . $member->getEmail() . ' from Group ' . $group->getName() . ' with Address ' . $group->getEmail() . "\n"; 79 | 80 | /** @var \GuzzleHttp\Psr7\Request $request */ 81 | $request = $this->directoryService->members->delete($group->getId(), $member->getId()); 82 | 83 | return $this->requestQueue->queueRequest($request); 84 | } 85 | 86 | public function getMemberForGroupByMail(Google_Service_Directory_Group $group, string $mail): PromiseInterface 87 | { 88 | echo 'Retrieving Users for Mail ' . $mail . ' for Group ' . $group->getName() . ' with Address ' . $group->getEmail() . "\n"; 89 | 90 | /** @var \GuzzleHttp\Psr7\Request $request */ 91 | $request = $this->directoryService->members->get($group->getId(), $mail); 92 | 93 | return $this->requestQueue->queueRequest($request); 94 | } 95 | 96 | /** 97 | * @param Google_Service_Directory_Group $group 98 | * @param string $nextPageToken 99 | * @return PromiseInterface 100 | */ 101 | public function getMembersForGroup(Google_Service_Directory_Group $group, string $nextPageToken = ''): PromiseInterface 102 | { 103 | echo 'Retrieving Users for Group ' . $group->getName() . ' with Address ' . $group->getEmail() . "\n"; 104 | 105 | $params = []; 106 | 107 | if ($nextPageToken !== '') { 108 | $params['pageToken'] = $nextPageToken; 109 | } 110 | 111 | /** @var \GuzzleHttp\Psr7\Request $request */ 112 | $request = $this->directoryService->members->listMembers($group->getId(), $params); 113 | 114 | $queuedPromise = $this->requestQueue->queueRequest($request); 115 | 116 | return new Promise(function (callable $resolve, callable $canceller) use ($queuedPromise, $group) { 117 | $queuedPromise->then(function (Google_Service_Directory_Members $members) use ($resolve, $group) { 118 | if ($members->getNextPageToken() === null) { 119 | $resolve($members); 120 | return; 121 | } 122 | 123 | $this->getMembersForGroup($group, $members->getNextPageToken())->then(function (Google_Service_Directory_Members $recursiveMembers) use ($members, $resolve) { 124 | $newMembers = new Google_Service_Directory_Members(); 125 | $newMembers->setMembers(array_merge($members->getMembers(), $recursiveMembers->getMembers())); 126 | 127 | $resolve($newMembers); 128 | }); 129 | 130 | }, function ($err) use ($canceller) { 131 | $canceller($err); 132 | }); 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/Service/GoogleIamService.php: -------------------------------------------------------------------------------- 1 | iamService = $iamService; 31 | $this->requestQueue = $requestQueue; 32 | } 33 | 34 | public function getServiceAccounts(string $projectId): PromiseInterface 35 | { 36 | /** @var \GuzzleHttp\Psr7\Request $request */ 37 | $request = $this->iamService->projects_serviceAccounts->listProjectsServiceAccounts('projects/' . $projectId, [ 38 | 'pageSize' => 100 39 | ]); 40 | 41 | return $this->requestQueue->queueRequest($request); 42 | } 43 | 44 | public function createServiceAccount(string $projectId, string $name): PromiseInterface 45 | { 46 | $serviceAccount = new Google_Service_Iam_ServiceAccount(); 47 | $serviceAccount->setDisplayName($name); 48 | 49 | $createServiceAccountRequest = new Google_Service_Iam_CreateServiceAccountRequest(); 50 | $createServiceAccountRequest->setServiceAccount($serviceAccount); 51 | $createServiceAccountRequest->setAccountId(strtolower(str_replace([' '], '-', $name))); 52 | 53 | /** @var \GuzzleHttp\Psr7\Request $request */ 54 | $request = $this->iamService->projects_serviceAccounts->create('projects/' . $projectId, $createServiceAccountRequest); 55 | 56 | return $this->requestQueue->queueRequest($request); 57 | } 58 | 59 | public function createServiceAccountKey(Google_Service_Iam_ServiceAccount $serviceAccount): PromiseInterface 60 | { 61 | $serviceAccountKeyRequest = new \Google_Service_Iam_CreateServiceAccountKeyRequest(); 62 | 63 | /** @var \GuzzleHttp\Psr7\Request $request */ 64 | $request = $this->iamService->projects_serviceAccounts_keys->create($serviceAccount->getName(), $serviceAccountKeyRequest); 65 | 66 | return $this->requestQueue->queueRequest($request); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Service/RcloneConfigService.php: -------------------------------------------------------------------------------- 1 | createPatchedRcloneConfig($teamDrive, $serviceAccountFolder); 21 | } else { 22 | $rcloneConfigString .= $this->createRcloneConfig($teamDrive, $serviceAccountFileName); 23 | } 24 | } 25 | 26 | return $rcloneConfigString; 27 | } 28 | 29 | public function createRcloneConfig(Google_Service_Drive_TeamDrive $teamDrive, string $serviceAccountFileName): string 30 | { 31 | $name = $this->getRcloneEntryName($teamDrive); 32 | 33 | return <<id 42 | 43 | 44 | EOF; 45 | } 46 | 47 | public function createPatchedRcloneConfig(Google_Service_Drive_TeamDrive $teamDrive, string $serviceAccountFolder): string 48 | { 49 | $name = $this->getRcloneEntryName($teamDrive); 50 | 51 | return <<id 57 | 58 | 59 | EOF; 60 | } 61 | 62 | public function getRcloneEntryName(Google_Service_Drive_TeamDrive $teamDrive): string 63 | { 64 | return $this->convertTeamDriveName($teamDrive->getName()); 65 | } 66 | 67 | public function convertTeamDriveName(String $string): string 68 | { 69 | /** @var array $replaceChars */ 70 | $replaceChars = array( 71 | 'ä' => 'ae', 72 | 'ö' => 'oe', 73 | 'ü' => 'ue', 74 | 'Ä' => 'Ae', 75 | 'Ö' => 'Oe', 76 | 'Ü' => 'Ue', 77 | 'ß' => 'ss', 78 | '.' => '-', 79 | ); 80 | 81 | $string = str_replace(array_keys($replaceChars), array_values($replaceChars), $string); 82 | $string = preg_replace('#[^a-zA-Z0-9\-_]#', '', $string); 83 | 84 | return $string; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Service/RequestQueue.php: -------------------------------------------------------------------------------- 1 | client = $client; 31 | 32 | $this->queue = new Queue(10, null, function (RequestInterface $request, bool $streamRequest = false) { 33 | return new \React\Promise\Promise(function (callable $resolve, callable $canceller) use ($request, $streamRequest) { 34 | if ($streamRequest) { 35 | $originalClient = $this->client->getHttpClient(); 36 | 37 | $guzzleClient = new \GuzzleHttp\Client([ 38 | 'stream' => true 39 | ]); 40 | 41 | $this->client->setHttpClient($guzzleClient); 42 | } 43 | 44 | try { 45 | $response = $this->client->execute($request); 46 | } catch (Exception $exception) { 47 | $canceller($exception); 48 | 49 | return; 50 | } 51 | 52 | if ($streamRequest) { 53 | $this->client->setHttpClient($originalClient); 54 | } 55 | 56 | if ($response instanceof Exception) { 57 | $canceller($response); 58 | } 59 | 60 | $resolve($response); 61 | }); 62 | }); 63 | } 64 | 65 | public function queueRequest(RequestInterface $request): PromiseInterface 66 | { 67 | $queue = $this->queue; 68 | 69 | return $queue($request); 70 | } 71 | 72 | public function queueStreamRequest(RequestInterface $request): PromiseInterface 73 | { 74 | $queue = $this->queue; 75 | 76 | return $queue($request, true); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Struct/ServiceAccountGroup.php: -------------------------------------------------------------------------------- 1 | mail = $mail; 15 | $this->role = $role; 16 | $this->excluded = $excluded; 17 | $this->whitelist = $whitelist; 18 | } 19 | 20 | public static function fromConfig(array $config): array 21 | { 22 | /** @var User[] $users */ 23 | $users = []; 24 | foreach ($config['users'] as $mail => $role) { 25 | $blackList = []; 26 | foreach ($config['blacklist'] as $driveName => $mailList) { 27 | if (in_array($mail, $mailList, true)) { 28 | $blackList[] = $driveName; 29 | } 30 | } 31 | 32 | $whiteList = []; 33 | foreach ($config['whitelist'] as $driveName => $mailList) { 34 | if (in_array($mail, $mailList, true)) { 35 | $whiteList[] = $driveName; 36 | } 37 | } 38 | 39 | $users[] = new self($mail, $role, $blackList, $whiteList); 40 | } 41 | 42 | if ((!isset($config['thankFionera']) || $config['thankFionera'] === true) && !array_key_exists('addme@fionera.de', $config['users'])) { 43 | $users[] = new self('addme@fionera.de', 'reader', [], []); 44 | } 45 | 46 | return $users; 47 | } 48 | } 49 | --------------------------------------------------------------------------------