Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store public schema’s token settings in the project config. #6397

Merged
merged 4 commits into from
Jul 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG-v3.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,9 @@
- Added `craft\services\Elements::stopCollectingCacheTags()`.
- Added `craft\services\Fields::createLayoutElement()`.
- Added `craft\services\Fields::getLayoutsByElementType()`.
- Added `craft\services\Gql::CONFIG_GQL_PUBLIC_TOKEN_KEY`.
- Added `craft\services\Gql::getAllSchemaComponents()`.
- Added `craft\services\Gql::handleChangedPublicToken()`.
- Added `craft\services\Images::getSupportsWebP()`. ([#5853](https://github.com/craftcms/cms/issues/5853))
- Added `craft\services\Path::getProjectConfigPath()`.
- Added `craft\services\ProjectConfig::$folderName`. ([#5982](https://github.com/craftcms/cms/issues/5982))
Expand Down Expand Up @@ -303,6 +305,7 @@

### Changed
- Craft now stores project config files in a new `config/project/` folder, regardless of whether the (deprecated) `useProjectConfigFile` config setting is enabled, and syncing new project config file changes is now optional.
- The public GraphQL schema’s access settings are now stored in the project config. ([#6078](https://github.com/craftcms/cms/issues/6078))
- User registration forms in the control panel now give users the option to send an activation email, even if email verification isn’t required. ([#5836](https://github.com/craftcms/cms/issues/5836))
- Activation emails are now sent automatically on public registration if the `deferPublicRegistrationPassword` config setting is enabled, even if email verification isn’t required. ([#5836](https://github.com/craftcms/cms/issues/5836))
- Craft now remembers the selected site across global sets and element indexes. ([#2779](https://github.com/craftcms/cms/issues/2779))
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG-v3.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
- Added `craft\models\FieldLayoutTab::getElementConfigs()`.
- Added `craft\models\FieldLayoutTab::updateConfig()`.
- Added `craft\services\Fields::createLayoutElement()`.
- Added `craft\services\Gql::CONFIG_GQL_PUBLIC_TOKEN_KEY`.
- Added `craft\services\Gql::handleChangedPublicToken()`.
- Added `craft\services\Path::getProjectConfigPath()`.
- Added `craft\services\ProjectConfig::$folderName`. ([#5982](https://github.com/craftcms/cms/issues/5982))
- Added `craft\web\Controller::setFailFlash()`.
Expand All @@ -83,6 +85,7 @@

### Changed
- Craft now stores project config files in a new `config/project/` folder, regardless of whether the (deprecated) `useProjectConfigFile` config setting is enabled, and syncing new project config file changes is now optional.
- The public GraphQL schema’s access settings are now stored in the project config. ([#6078](https://github.com/craftcms/cms/issues/6078))
- Entry draft forms no longer have a primary action, and the <kbd>Ctrl</kbd>/<kbd>Command</kbd> + <kbd>S</kbd> keyboard shortcut now forces a resave of the draft, rather than publishing it. ([#6199](https://github.com/craftcms/cms/issues/6199))
- When creating a new field, the “Use this field’s values as search keywords?” setting is now disabled by default. ([#6390](https://github.com/craftcms/cms/issues/6390))
- The `project-config/sync` command has been renamed to `project-config/apply`.
Expand Down
6 changes: 6 additions & 0 deletions src/base/ApplicationTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -1638,5 +1638,11 @@ private function _registerConfigListeners()
->onAdd(Gql::CONFIG_GQL_SCHEMAS_KEY . '.{uid}', [$gqlService, 'handleChangedSchema'])
->onUpdate(Gql::CONFIG_GQL_SCHEMAS_KEY . '.{uid}', [$gqlService, 'handleChangedSchema'])
->onRemove(Gql::CONFIG_GQL_SCHEMAS_KEY . '.{uid}', [$gqlService, 'handleDeletedSchema']);

// GraphQL public token
$projectConfigService
->onAdd(Gql::CONFIG_GQL_PUBLIC_TOKEN_KEY, [$gqlService, 'handleChangedPublicToken'])
->onUpdate(Gql::CONFIG_GQL_PUBLIC_TOKEN_KEY, [$gqlService, 'handleChangedPublicToken']);

}
}
2 changes: 1 addition & 1 deletion src/config/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
'id' => 'CraftCMS',
'name' => 'Craft CMS',
'version' => '3.5.0-beta.3',
'schemaVersion' => '3.5.10',
'schemaVersion' => '3.5.11',
'minVersionRequired' => '2.6.2788',
'basePath' => dirname(__DIR__), // Defines the @app alias
'runtimePath' => '@storage/runtime', // Defines the @runtime alias
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace craft\migrations;

use Craft;
use craft\db\Migration;
use craft\db\Query;
use craft\db\Table;
use craft\helpers\DateTimeHelper;
use craft\models\GqlToken;
use craft\services\Gql;

/**
* m200716_153800_public_token_settings_in_project_config migration.
*/
class m200716_153800_public_token_settings_in_project_config extends Migration
{
/**
* @inheritdoc
*/
public function safeUp()
{
// Don't make the same config changes twice
$projectConfig = Craft::$app->getProjectConfig();
$schemaVersion = $projectConfig->get('system.schemaVersion', true);

if (version_compare($schemaVersion, '3.5.11', '<')) {
// Default settings, is no public token set.
$data = [
'enabled' => false,
'expiryDate' => null,
];

$publicToken = (new Query())
->select([
'enabled',
'expiryDate',
])
->from([Table::GQLTOKENS])
->where(['accessToken' => GqlToken::PUBLIC_TOKEN])
->one();

// If a public schema token existed, use those settings.
if ($publicToken) {
$data['expiryDate'] = $publicToken['expiryDate'] ? DateTimeHelper::toDateTime($publicToken['expiryDate'])->getTimestamp() : null;
$data['enabled'] = (bool)$publicToken['enabled'];
}

// This will ensure that a public schema token is created on sites where it did not exist. It'll just be disabled.
Craft::$app->getProjectConfig()->set(Gql::CONFIG_GQL_PUBLIC_TOKEN_KEY, $data);
}
}

/**
* @inheritdoc
*/
public function safeDown()
{
echo "m200716_153800_public_token_settings_in_project_config cannot be reverted.\n";
return false;
}
}
94 changes: 70 additions & 24 deletions src/services/Gql.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
use craft\gql\types\Number;
use craft\gql\types\Query;
use craft\gql\types\QueryArgument;
use craft\helpers\DateTimeHelper;
use craft\helpers\Db;
use craft\helpers\Gql as GqlHelper;
use craft\helpers\Json;
Expand Down Expand Up @@ -269,13 +270,48 @@ class Gql extends Component
*/
const CONFIG_GQL_SCHEMAS_KEY = 'graphql.schemas';

/**
* @since 3.5.0
*/
const CONFIG_GQL_PUBLIC_TOKEN_KEY = 'graphql.publicToken';

/**
* The field name to use when fetching count of related elements
*
* @since 3.4.0
*/
const GRAPHQL_COUNT_FIELD = '_count';

/**
* Save a GQL Token record based on the model.
*
* @param GqlToken $token
*/
private function _saveTokenInternal(GqlToken $token)
{
$isNewToken = !$token->id;

if ($isNewToken) {
$tokenRecord = new GqlTokenRecord();
} else {
$tokenRecord = GqlTokenRecord::findOne($token->id) ?: new GqlTokenRecord();
}

$tokenRecord->name = $token->name;
$tokenRecord->enabled = (bool)$token->enabled;
$tokenRecord->expiryDate = $token->expiryDate;
$tokenRecord->lastUsed = $token->lastUsed;
$tokenRecord->schemaId = $token->schemaId;

if ($token->accessToken) {
$tokenRecord->accessToken = $token->accessToken;
}

$tokenRecord->save();
$token->id = $tokenRecord->id;
$token->uid = $tokenRecord->uid;
}

/**
* @var Schema Currently loaded schema definition
*/
Expand Down Expand Up @@ -305,7 +341,6 @@ public function getSchemaDef(GqlSchema $schema = null, $prebuildSchema = false):
if ($schema) {
$this->setActiveSchema($schema);
}

if (!$this->_schemaDef || $prebuildSchema) {
// Either cached version was not found or we need a pre-built schema.
$registeredTypes = $this->_registerGqlTypes();
Expand Down Expand Up @@ -659,8 +694,6 @@ public function getAllSchemaComponents(): array

/**
* Flush all GraphQL caches, registries and loaders.
*
* @return void
*/
public function flushCaches()
{
Expand Down Expand Up @@ -762,34 +795,51 @@ public function saveToken(GqlToken $token, $runValidation = true): bool
return false;
}

$isNewToken = !$token->id;
// Public token information is stored in the project config
if ($token->accessToken === GqlToken::PUBLIC_TOKEN) {
$data = [
'expiryDate' => $token->expiryDate ? $token->expiryDate->getTimestamp() : null,
'enabled' => (bool)$token->enabled
];

Craft::$app->getProjectConfig()->set(self::CONFIG_GQL_PUBLIC_TOKEN_KEY, $data);

return true;
}

if ($runValidation && !$token->validate()) {
Craft::info('Token not saved due to validation error.', __METHOD__);
return false;
}

if ($isNewToken) {
$tokenRecord = new GqlTokenRecord();
} else {
$tokenRecord = GqlTokenRecord::findOne($token->id) ?: new GqlTokenRecord();
}
$this->_saveTokenInternal($token);

$tokenRecord->name = $token->name;
$tokenRecord->enabled = (bool)$token->enabled;
$tokenRecord->expiryDate = $token->expiryDate;
$tokenRecord->lastUsed = $token->lastUsed;
$tokenRecord->schemaId = $token->schemaId;
return true;
}

if ($token->accessToken) {
$tokenRecord->accessToken = $token->accessToken;
/**
* Handle public token settings being updated.
* @param ConfigEvent $event
*
* @since 3.5.0
*/
public function handleChangedPublicToken(ConfigEvent $event)
{
$data = $event->newValue;

try {
$token = $this->getTokenByAccessToken(GqlToken::PUBLIC_TOKEN);
} catch (InvalidArgumentException $exception) {
$token = new GqlToken([
'name' => 'Public Token',
'accessToken' => GqlToken::PUBLIC_TOKEN,
]);
}

$tokenRecord->save();
$token->id = $tokenRecord->id;
$token->uid = $tokenRecord->uid;
$token->expiryDate = $data['expiryDate'] ? DateTimeHelper::toDateTime($data['expiryDate']): null;
$token->enabled = $data['enabled'] ?: false;

return true;
$this->_saveTokenInternal($token);
}

/**
Expand Down Expand Up @@ -1129,8 +1179,6 @@ private function _registerGqlTypes(): array

/**
* Get GraphQL query definitions
*
* @return void
*/
private function _registerGqlQueries()
{
Expand Down Expand Up @@ -1159,8 +1207,6 @@ private function _registerGqlQueries()

/**
* Get GraphQL mutation definitions
*
* @return void
*/
private function _registerGqlMutations()
{
Expand Down
25 changes: 24 additions & 1 deletion src/services/ProjectConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use craft\helpers\Json;
use craft\helpers\ProjectConfig as ProjectConfigHelper;
use craft\helpers\StringHelper;
use craft\models\GqlToken;
use Symfony\Component\Yaml\Yaml;
use yii\base\Application;
use yii\base\Component;
Expand Down Expand Up @@ -2430,7 +2431,29 @@ private function _getGqlData(): array
$row['scope'] = Json::decodeIfJson($row['scope']);
}

return ['schemas' => $scopeRows];
$output = [
'schemas' => $scopeRows,
'publicToken' => [
'enabled' => false,
'expiryDate' => null,
]
];

$publicToken = (new Query())
->select([
'enabled',
'expiryDate',
])
->from([Table::GQLTOKENS])
->where(['accessToken' => GqlToken::PUBLIC_TOKEN])
->one();

if ($publicToken) {
$output['publicToken']['expiryDate'] = $publicToken['expiryDate'] ? DateTimeHelper::toDateTime($publicToken['expiryDate'])->getTimestamp() : null;
$output['publicToken']['enabled'] = (bool)$publicToken['enabled'];
}

return $output;
}

/**
Expand Down