Commit 788ce0dc authored by Thomas Löffler's avatar Thomas Löffler

Merge branch 'develop' into 'main'

Release 19-01-2021

See merge request services/extensions.typo3.org/ter!633
parents 01271156 136b8d0b
Pipeline #10264 canceled with stages
in 3 minutes and 21 seconds
include:
- project: 't3o/t3olayout'
- project: 'services/common/t3o-basic-pipeline-jobs'
ref: main
file: '/Configuration/GitLab/t3o-builds.yml'
file: 't3o-basic-pipeline-jobs.yml'
stages:
- maintenance
......
This diff is collapsed.
......@@ -113,6 +113,8 @@ class PackagistCommand extends Command
$downloadData = json_decode(file_get_contents($packagistUrl, false, $context), true);
foreach ($downloadData['package']['downloads']['versions'] as $version => $downloads) {
// To ensure packagist version with "v" in tag also gets counted e.g. v4.2.3 is 4.2.3 in TER.
$version = ltrim($version, 'v');
$versionFound = $this->versionRepository->findOneByExtensionAndVersionString($extension, $version);
if (null === $versionFound) {
continue;
......
......@@ -54,6 +54,7 @@ class TokenController extends ActionController implements LoggerAwareInterface
$this->view->assignMultiple([
'actions' => self::API_ACTIONS,
'activeTab' => $this->getActiveTab(),
'expires' => strtotime('+7 day', (int)$this->context->getPropertyFromAspect('date', 'timestamp')),
'extensions' => $this->extensionRepository->findByFrontendUser(
(string)$this->context->getPropertyFromAspect('frontend.user', 'username')
)
......
......@@ -81,7 +81,12 @@ class DownloadRepository extends AbstractRepository
*/
public function findDownloadCounterByMonthAndExtensionKey($month, $extensionKey, $versionId, $source)
{
$querySettings = GeneralUtility::makeInstance(Typo3QuerySettings::class);
$querySettings->setRespectStoragePage(false);
$query = $this->createQuery();
$query->setQuerySettings($querySettings);
$query->matching(
$query->logicalAnd(
[
......
......@@ -135,7 +135,9 @@ class ExtensionIndexService implements LoggerAwareInterface
$versionObj->appendChild(new \DOMElement('title', $this->xmlentities((string)$extensionVersionArr['title'])));
$versionObj->appendChild(new \DOMElement('description', $this->xmlentities((string)$extensionVersionArr['description'])));
$versionObj->appendChild(new \DOMElement('state', $this->xmlentities((string)$extensionVersionArr['state'])));
$versionObj->appendChild(new \DOMElement('reviewstate', (string)$extensionVersionArr['review_state']));
// Use "0" for outdated extension versions as the Extension Manager handles it as insecure
$reviewState = (string)((int)$extensionVersionArr['review_state'] !== -2 ? $extensionVersionArr['review_state'] : 0);
$versionObj->appendChild(new \DOMElement('reviewstate', $reviewState));
$versionObj->appendChild(new \DOMElement('category', $this->xmlentities((string)$extensionVersionArr['em_category'])));
if ($extensionVersionArr['category'] === 'distribution') {
$prefixDistributionFilePath = $extensionKey[0] . '/' . $extensionKey[1] . '/' . $extensionKey . '_' . $versionNumber . '_';
......
......@@ -25,7 +25,7 @@ use TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator;
*/
class TokenCreationValidator extends AbstractValidator
{
private const MAX_LIFETIME = 8035200;
private const MAX_LIFETIME = 31536000;
private const AVAILABLE_SCOPES = [
'extension:read',
'extension:write',
......
<div class="row">
<div class="col-md-8">
<p>
Enter a name, select the expiration date (max 3 months), the scopes and the extensions for the token.
Enter a name, select the expiration date (max 1 year), the scopes and the extensions for the token.
Approve by entering your password.
</p>
</div>
......@@ -16,7 +16,7 @@
<div class="form-group row">
<div class="col-md-8">
<label for="tokenExpires">Expiration date</label>
<f:form.textfield type="date" class="form-control" id="tokenExpires" property="expires" value="" />
<f:form.textfield type="date" class="form-control" id="tokenExpires" property="expires" value="{expires -> f:format.date(format: 'Y-m-d')}" />
</div>
</div>
<div class="form-group row">
......
......@@ -20,7 +20,8 @@
<div class="tab-content">
<f:for each="{actions}" as="action">
<div class="tab-pane mt-3 {f:if(condition: '{activeTab} == {action}', then: 'active')}" id="{action}" role="tabpanel">
<f:render partial="Token/{action -> f:format.case(mode: 'capital')}" arguments="{settings: settings, extensions: extensions}" />
<f:render partial="Token/{action -> f:format.case(mode: 'capital')}"
arguments="{settings: settings, expires: expires, extensions: extensions}" />
</div>
</f:for>
</div>
......
......@@ -33,7 +33,7 @@ class ExtensionIndexServiceTest extends UnitTestCase
<title/>
<description/>
<state/>
<reviewstate/>
<reviewstate>0</reviewstate>
<category/>
<downloadcounter/>
<lastuploaddate/>
......@@ -54,7 +54,7 @@ class ExtensionIndexServiceTest extends UnitTestCase
<title/>
<description/>
<state/>
<reviewstate/>
<reviewstate>0</reviewstate>
<category/>
<downloadcounter/>
<lastuploaddate/>
......@@ -75,7 +75,7 @@ class ExtensionIndexServiceTest extends UnitTestCase
<title/>
<description/>
<state/>
<reviewstate/>
<reviewstate>0</reviewstate>
<category/>
<downloadcounter/>
<lastuploaddate/>
......@@ -89,6 +89,48 @@ class ExtensionIndexServiceTest extends UnitTestCase
<t3xfilemd5/>
<documentation_link/>
</version>
</extension>
<extension extensionkey="insecure">
<downloadcounter>131313</downloadcounter>
<version version="2.0.0">
<title/>
<description/>
<state/>
<reviewstate>-1</reviewstate>
<category/>
<downloadcounter/>
<lastuploaddate/>
<uploadcomment/>
<dependencies/>
<composerinfo/>
<authorname/>
<authoremail/>
<authorcompany>Security</authorcompany>
<ownerusername>thomas</ownerusername>
<t3xfilemd5/>
<documentation_link/>
</version>
</extension>
<extension extensionkey="outdated">
<downloadcounter>131313</downloadcounter>
<version version="2.0.0">
<title/>
<description/>
<state/>
<reviewstate>0</reviewstate>
<category/>
<downloadcounter/>
<lastuploaddate/>
<uploadcomment/>
<dependencies/>
<composerinfo/>
<authorname/>
<authoremail/>
<authorcompany>Old</authorcompany>
<ownerusername>thomas</ownerusername>
<t3xfilemd5/>
<documentation_link/>
</version>
</extension>
</extensions>
';
......@@ -100,6 +142,7 @@ class ExtensionIndexServiceTest extends UnitTestCase
'versions' => [
'1.0.0' => [
'authorcompany' => 'ACME',
'review_state' => 0,
'dependencies' => '[{"kind":"depends","extensionKey":"typo3","versionRange":"8.7.0-10.4.99"}]'
]
]
......@@ -110,6 +153,7 @@ class ExtensionIndexServiceTest extends UnitTestCase
'versions' => [
'1.5.0' => [
'authorcompany' => 't3o',
'review_state' => 0,
'dependencies' => '[]'
]
]
......@@ -120,6 +164,29 @@ class ExtensionIndexServiceTest extends UnitTestCase
'versions' => [
'2.0.0' => [
'authorcompany' => 'INC',
'review_state' => 0,
'dependencies' => null
]
]
],
'insecure' => [
'downloads' => 131313,
'frontend_user' => 'thomas',
'versions' => [
'2.0.0' => [
'authorcompany' => 'Security',
'review_state' => -1,
'dependencies' => null
]
]
],
'outdated' => [
'downloads' => 131313,
'frontend_user' => 'thomas',
'versions' => [
'2.0.0' => [
'authorcompany' => 'Old',
'review_state' => -2,
'dependencies' => null
]
]
......@@ -130,6 +197,6 @@ class ExtensionIndexServiceTest extends UnitTestCase
$resultString = $result->saveXML();
// remove the lines with "<!--Index created
$resultString = preg_replace('/\<!--Index created(.*)--\>\n/m', '', $resultString);
self::assertEquals($resultString, $expected);
self::assertEquals($expected, $resultString);
}
}
......@@ -180,7 +180,8 @@ CREATE TABLE `tx_terfe2_domain_model_download` (
KEY `source` (source),
KEY `downloadsByMonth` (`extension_key`, `source`, `month`),
KEY `downloadsByMonthAndVersion` (`extension_key`, `source`, `month`, `version_id`),
KEY `downloadsByVersion` (`extension_key`, `source`, `version_id`)
KEY `downloadsByVersion` (`extension_key`, `source`, `version_id`),
KEY `index_version` (`version_id`)
);
# ======================================================================
......
......@@ -92,13 +92,14 @@ abstract class AbstractGrant implements GrantInterface
protected function issueAccessToken(ServerRequestInterface $request): AccessToken
{
$user = $request->getAttribute('api.user');
$requestedScope = $this->getScope($request, $user);
$accessToken = new AccessToken(
$this->random->generateRandomHexString($this->configurationService->getRandomLength()),
$user->getId(),
$this->timestamp,
$this->getExpires($request),
$this->getScope($request, $user),
$this->getExtensions($request, $user),
$requestedScope,
$this->getExtensions($request, $user, $requestedScope),
$this->getName($request),
);
......@@ -173,12 +174,23 @@ abstract class AbstractGrant implements GrantInterface
return $scope;
}
protected function getExtensions(ServerRequestInterface $request, ApiUserInterface $user): string
{
protected function getExtensions(
ServerRequestInterface $request,
ApiUserInterface $user,
int $requestedScope
): string {
$extensions = $request->getAttribute('routing')->offsetGet('routeArguments')->getPropertyValue('extensions');
$allowedExtensions = $this->extensionRepository->findExtensionKeysForUser($user->getName());
if ($extensions === null || $allowedExtensions === [] || $user->getScope()->isController()) {
// Skip restricting extensions if non were requested, the user does not have any
// extensions to restrict or in case the requesting user (the current API user)
// is a controller and also requested a controller scope. The latter means the
// token will allow the user to access all extensions since it will be a so called
// "controller" token, so we don't have to add any restrictions.
if ($extensions === null
|| $allowedExtensions === []
|| ($user->getScope()->isController() && (new Scope($requestedScope))->isController())
) {
return '';
}
......
......@@ -66,7 +66,7 @@ abstract class AbstractService
{
if (ExtensionManagementUtility::isLoaded('solr')) {
$garbageCollector = GeneralUtility::makeInstance(GarbageCollector::class);
$garbageCollector->collectGarbage('tx_terfe2_domain_model_extension', $this->key);
$garbageCollector->collectGarbage('tx_terfe2_domain_model_extension', $this->extensionUid);
}
return $this;
......@@ -76,7 +76,7 @@ abstract class AbstractService
{
if (ExtensionManagementUtility::isLoaded('solr')) {
$indexQueue = GeneralUtility::makeInstance(Queue::class);
$indexQueue->updateItem('tx_terfe2_domain_model_extension', $this->key);
$indexQueue->updateItem('tx_terfe2_domain_model_extension', $this->extensionUid);
}
return $this;
......@@ -86,7 +86,7 @@ abstract class AbstractService
{
if (ExtensionManagementUtility::isLoaded('solr')) {
$indexQueue = GeneralUtility::makeInstance(Queue::class);
$indexQueue->deleteItem('tx_terfe2_domain_model_extension', $this->key);
$indexQueue->deleteItem('tx_terfe2_domain_model_extension', $this->extensionUid);
}
return $this;
......
......@@ -209,7 +209,7 @@
"style": "form",
"explode": true,
"schema": {
"maximum": 8035200,
"maximum": 31536000,
"minimum": 60,
"type": "integer",
"default": 604800
......@@ -3227,7 +3227,7 @@
"style": "form",
"explode": true,
"schema": {
"maximum": 8035200,
"maximum": 31536000,
"minimum": 60,
"type": "integer",
"default": 604800
......
......@@ -146,7 +146,7 @@ paths:
style: form
explode: true
schema:
maximum: 8035200
maximum: 31536000
minimum: 60
type: integer
default: 604800
......@@ -2132,7 +2132,7 @@ components:
style: form
explode: true
schema:
maximum: 8035200
maximum: 31536000
minimum: 60
type: integer
default: 604800
......
......@@ -10,12 +10,12 @@ return [
'Connections' => [
'Default' => [
'charset' => 'utf8',
'dbname' => getenv('DB_NAME'),
'dbname' => false,
'driver' => 'mysqli',
'host' => getenv('DB_HOST'),
'password' => getenv('DB_PASSWORD'),
'host' => false,
'password' => false,
'port' => 3306,
'user' => getenv('DB_USERNAME'),
'user' => false,
],
],
],
......@@ -59,12 +59,12 @@ return [
'TYPO3FEUserExist' => '0',
'enableBELDAPAuthentication' => '0',
'enableBESSO' => '0',
'enableFELDAPAuthentication' => '1',
'enableFELDAPAuthentication' => '0',
'enableFESSO' => '0',
'forceLowerCaseUsername' => '0',
'keepBEGroups' => '0',
'keepBESSODomainName' => '0',
'keepFEGroups' => '1',
'keepFEGroups' => '0',
'keepFESSODomainName' => '0',
'throwExceptionAtLogin' => '1',
'useExtConfConfiguration' => '0',
......@@ -74,52 +74,27 @@ return [
'showSampleTasks' => '1',
],
'solr' => [
'allowLegacySiteMode' => '0',
'allowSelfSignedCertificates' => '0',
'useConfigurationFromClosestTemplate' => '0',
'useConfigurationMonitorTables' => '',
'useConfigurationTrackRecordsOutsideSiteroot' => '1',
],
't3olayout' => [
'loginFormPid' => '12',
'loginPid' => '8',
'loginFormPid' => '',
'loginPid' => '',
],
'ter' => [
'repositoryDir' => '/var/www/html/private/fileadmin/ter/',
'repositoryDir' => '',
],
'ter_rest' => [
'routing' => [
'routeArguments' => [
'String' => \T3o\TerRest\Routing\RouteArgument\StringArgument::class,
'Integer' => \T3o\TerRest\Routing\RouteArgument\IntegerArgument::class,
'Object' => \T3o\TerRest\Routing\RouteArgument\ObjectArgument::class,
'Array' => \T3o\TerRest\Routing\RouteArgument\ArrayArgument::class
],
'formData' => [
'String' => \T3o\TerRest\Routing\FormData\StringData::class,
'Integer' => \T3o\TerRest\Routing\FormData\IntegerData::class,
'Boolean' => \T3o\TerRest\Routing\FormData\BooleanData::class,
'Binary' => \T3o\TerRest\Routing\FormData\BinaryData::class
],
'responseTypes' => [
'*/*' => \TYPO3\CMS\Core\Http\JsonResponse::class,
'application/json' => \TYPO3\CMS\Core\Http\JsonResponse::class
],
'enableFeUserAuthentication' => true
],
'extensionVersionFiles' => '.t3x,.gif,.png,.svg,_Distribution.png,_Distribution.svg,_DistributionWelcome.png,_DistributionWelcome.svg',
'extensionMaxUploadSize' => 31457280,
'extensionKeyPattern' => '^((?!(tx|user|pages|tt|sys|ts_language|csh))[a-z]{1}[a-z0-9_]+)$',
'apiPath' => '/api/',
'schemaPath' => 'EXT:ter_rest/Resources/Private/Schema/',
'extensionDownloadUrl' => 'https://extensions.typo3.org/extension/download/',
'abandonUser' => 'abandoned_extensions',
'randomLength' => getenv('TER_REST_RANDOM_LENGTH'),
'defaultLifetime' => getenv('TER_REST_DEFAULT_LIFETIME'),
'jwtSubject' => getenv('TER_REST_JWT_SUBJECT'),
'jwtLatency' => getenv('TER_REST_JWT_LATENCY'),
'signatureIdentifier' => getenv('TER_REST_SIGNATURE_IDENTIFIER')
]
'abandonUser' => '',
'apiPath' => '',
'extensionDownloadUrl' => '',
'extensionKeyPattern' => '',
'extensionMaxUploadSize' => '',
'extensionVersionFiles' => '',
'schemaPath' => '',
],
],
'FE' => [
'cacheHash' => [
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment