diff --git a/classes/Rest/Controllers/BaseControllerProvider.php b/classes/Rest/Controllers/BaseControllerProvider.php index 3c7d66eea7..dbb1b5fa1f 100644 --- a/classes/Rest/Controllers/BaseControllerProvider.php +++ b/classes/Rest/Controllers/BaseControllerProvider.php @@ -2,6 +2,8 @@ namespace Rest\Controllers; +use Rest\Exceptions\BadTokenException; +use Rest\Exceptions\EmptyTokenException; use DateTime; use Models\Services\Tokens; use Rest\Utilities\Authentication; @@ -221,6 +223,7 @@ public function authorize(Request $request, array $requirements = array()) throw new UnauthorizedHttpException('xdmod', self::EXCEPTION_MESSAGE); } + $authorized = $user->hasAcls($requirements); if ($authorized === false && !$isPublicUser) { throw new AccessDeniedHttpException(self::EXCEPTION_MESSAGE); @@ -752,7 +755,8 @@ protected function getTimestamp($date, $paramName = 'date', $format = 'Y-m-d') * * @param Request $request * @return \XDUser - * @throws BadRequestHttpException if the provided token is empty, or there is not a provided token. + * @throws EmptyTokenException if the provided token is empty, or there is not a provided token. + * @throws BadTokenException if the provided token is in an invalid format. * @throws \Exception if the user's token from the db does not validate against the provided token. */ protected function authenticateToken($request) @@ -779,20 +783,21 @@ protected function authenticateToken($request) // If it's still empty, then no token == no access. if (empty($rawToken)) { - throw new UnauthorizedHttpException( + throw new EmptyTokenException( Tokens::HEADER_KEY, 'No Token Provided.' ); - } + } // We expect the token to be in the form /^(\d+).(.*)$/ so just make sure it at least has the required delimiter. $delimPosition = strpos($rawToken, Tokens::DELIMITER); if ($delimPosition === false) { - throw new UnauthorizedHttpException( + throw new BadTokenException( Tokens::HEADER_KEY, 'Invalid token format.' ); + } $userId = substr($rawToken, 0, $delimPosition); diff --git a/classes/Rest/Controllers/WarehouseControllerProvider.php b/classes/Rest/Controllers/WarehouseControllerProvider.php index 3410143e2b..68870a3ea1 100644 --- a/classes/Rest/Controllers/WarehouseControllerProvider.php +++ b/classes/Rest/Controllers/WarehouseControllerProvider.php @@ -5,6 +5,7 @@ use CCR\DB; use CCR\Log; use Configuration\XdmodConfiguration; +use DataWarehouse; use DataWarehouse\Data\BatchDataset; use DataWarehouse\Export\RealmManager; use DataWarehouse\Query\Exceptions\AccessDeniedException; @@ -26,6 +27,9 @@ use DataWarehouse\Access\Usage; use stdClass; +use Rest\Exceptions\BadTokenException; +use Rest\Exceptions\EmptyTokenException; + /** * @SuppressWarnings(PHPMD.StaticAccess) **/ @@ -336,6 +340,9 @@ public function setupRoutes(Application $app, ControllerCollection $controller) $controller ->get("$root/raw-data", "$current::getRawData"); + + $controller + ->get("$root/search/dw_descripter", "$current::getDwDescripter"); } /** @@ -2532,4 +2539,143 @@ private function validateRawDataFilterParam( $filterValuesArray = explode(',', $filterValuesStr); return $filterValuesArray; } + + + public function getDwDescripter(Request $request, Application $app){ + $user = null; + try { + $user = $this->authenticateToken($request); + } catch (EmptyTokenException $e) { + $user = $this->getUserFromRequest($request); + } catch (Exception $e) { + throw new BadTokenException('xdmod', "An error was encountered while attempting to process the requested authorization procedure."); + } + + $roles = $user->getAllRoles(true); + + $roleDescriptors = array(); + foreach ($roles as $activeRole) { + $shortRole = $activeRole; + $us_pos = strpos($shortRole, '_'); + if ($us_pos > 0){ + $shortRole = substr($shortRole, 0, $us_pos); + } + + if (array_key_exists($shortRole, $roleDescriptors)) { + continue; + } + + $realms = array(); + $groupByObjects = array(); + $realmObjects = Realms::getRealmObjectsForUser($user); + $query_descripter_realms = Acls::getQueryDescripters($user); + + foreach($query_descripter_realms as $query_descripter_realm => $query_descripter_groups){ + + $category = DataWarehouse::getCategoryForRealm($query_descripter_realm); + if ($category === null) { + continue; + } + + $seenstats = array(); + + $realmObject = $realmObjects[$query_descripter_realm]; + $realmDisplay = $realmObject->getDisplay(); + $realms[$query_descripter_realm] = array( + 'text' => $query_descripter_realm, + 'category' => $realmDisplay, + 'dimensions' => array(), + 'metrics' => array(), + ); + + foreach($query_descripter_groups as $query_descripter_group) { + foreach ($query_descripter_group as $query_descripter) { + if ($query_descripter->getDisableMenu()) { + continue; + } + + $groupByName = $query_descripter->getGroupByName(); + $group_by_object = $query_descripter->getGroupByInstance(); + $permittedStatistics = $group_by_object->getRealm()->getStatisticIds(); + + $groupByObjects[$query_descripter_realm . '_' . $groupByName] = array( + 'object' => $group_by_object, + 'permittedStats' => $permittedStatistics); + $realms[$query_descripter_realm]['dimensions'][$groupByName] = array( + 'text' => $groupByName == 'none' ? 'None' : $group_by_object->getName(), + 'info' => $group_by_object->getHtmlDescription() + ); + + $stats = array_diff($permittedStatistics, $seenstats); + if (empty($stats)) { + continue; + } + + $statsObjects = $query_descripter->getStatisticsClasses($stats); + foreach ($statsObjects as $realm_group_by_statistic => $statistic_object) { + + if ( ! $statistic_object->showInMetricCatalog() ) { + continue; + } + + $semStatId = \Realm\Realm::getStandardErrorStatisticFromStatistic( + $realm_group_by_statistic + ); + $realms[$query_descripter_realm]['metrics'][$realm_group_by_statistic] = + array( + 'text' => $statistic_object->getName(), + 'info' => $statistic_object->getHtmlDescription(), + 'std_err' => in_array($semStatId, $permittedStatistics), + 'hidden_groupbys' => $statistic_object->getHiddenGroupBys() + ); + $seenstats[] = $realm_group_by_statistic; + } + } + } + + $texts = array(); + foreach($realms[$query_descripter_realm]['metrics'] as $key => $row) + { + $texts[$key] = $row['text']; + } + array_multisort($texts, SORT_ASC, $realms[$query_descripter_realm]['metrics']); + } + $texts = array(); + foreach($realms as $key => $row) + { + $texts[$key] = $row['text']; + } + array_multisort($texts, SORT_ASC, $realms); + + $roleDescriptors[$shortRole] = array('totalCount'=> 1, 'data' => array(array( 'realms' => $realms))); + } + + $combinedRealmDescriptors = array(); + foreach ($roleDescriptors as $roleDescriptor) { + foreach ($roleDescriptor['data'][0]['realms'] as $realm => $realmDescriptor) { + if (!isset($combinedRealmDescriptors[$realm])) { + $combinedRealmDescriptors[$realm] = array( + 'metrics' => array(), + 'dimensions' => array(), + 'text' => $realmDescriptor['text'], + 'category' => $realmDescriptor['category'], + ); + } + + $combinedRealmDescriptors[$realm]['metrics'] += $realmDescriptor['metrics']; + $combinedRealmDescriptors[$realm]['dimensions'] += $realmDescriptor['dimensions']; + } + } + + return $app->json( + [ 'success' => true, + 'totalCount' => 1, + 'data' => array( + array( + 'realms' => $combinedRealmDescriptors, + ), + ), + ] + ); + } } diff --git a/classes/Rest/Controllers/WarehouseExportControllerProvider.php b/classes/Rest/Controllers/WarehouseExportControllerProvider.php index c0c7ec54fd..0781386068 100644 --- a/classes/Rest/Controllers/WarehouseExportControllerProvider.php +++ b/classes/Rest/Controllers/WarehouseExportControllerProvider.php @@ -4,6 +4,8 @@ use CCR\DB; use CCR\Log; +use Rest\Exceptions\BadTokenException; +use Rest\Exceptions\EmptyTokenException; use DataWarehouse\Data\RawStatisticsConfiguration; use DataWarehouse\Export\FileManager; use DataWarehouse\Export\QueryHandler; @@ -97,15 +99,12 @@ public function getRealms(Request $request, Application $app) // to the normal session authentication if a token is not provided. try { $user = $this->authenticateToken($request); + } catch (EmptyTokenException $e) { + $user = $this->getUserFromRequest($request); } catch (Exception $e) { - // NOOP + throw new BadTokenException('xdmod', "An error was encountered while attempting to process the requested authorization procedure."); } - if ($user === null) { - $user = $this->authorize($request); - } - - $config = RawStatisticsConfiguration::factory(); $realms = array_map( diff --git a/classes/Rest/Exceptions/BadTokenException.php b/classes/Rest/Exceptions/BadTokenException.php new file mode 100644 index 0000000000..de5aebc1d0 --- /dev/null +++ b/classes/Rest/Exceptions/BadTokenException.php @@ -0,0 +1,23 @@ + $challenge); + + parent::__construct(401, $message, $previous, $headers, $code); + } +} diff --git a/classes/Rest/Exceptions/EmptyTokenException.php b/classes/Rest/Exceptions/EmptyTokenException.php new file mode 100644 index 0000000000..efc0e86008 --- /dev/null +++ b/classes/Rest/Exceptions/EmptyTokenException.php @@ -0,0 +1,23 @@ + $challenge); + + parent::__construct(401, $message, $previous, $headers, $code); + } +} diff --git a/tests/integration/lib/Rest/WarehouseControllerProviderTest.php b/tests/integration/lib/Rest/WarehouseControllerProviderTest.php index a88cc8bddf..fcf1c2f53e 100644 --- a/tests/integration/lib/Rest/WarehouseControllerProviderTest.php +++ b/tests/integration/lib/Rest/WarehouseControllerProviderTest.php @@ -873,4 +873,62 @@ public function provideGetRawData() } return $tests; } + + + /** + * @dataProvider provideGetDwDescripter + */ + + public function testGetDwDescripter($role, $tokenType) + { + parent::runTokenAuthTest( + $role, + $tokenType, + [ + 'path' => 'rest/warehouse/search/dw_descripter', + 'method' => 'get', + 'params' => null, + 'data' => null, + 'endpoint_type' => 'rest', + 'authentication_type' => 'token_optional', + 'want_public_user' => true + ], + parent::validateSuccessResponse(function ($body, $assertMessage) { + $this->assertSame(1, $body['totalCount'], $assertMessage); + foreach (['Jobs', 'Cloud', 'ResourceSpecifications', 'Storage'] as $realmName) { + $realm = $body['data'][0]['realms'][$realmName]; + foreach (['metrics', 'dimensions'] as $key) { + $this->assertArrayHasKey( + $key, + $realm, + $assertMessage . ": {$key} should be present in {$realmName}" + ); + foreach ($realm[$key] as $elementName => $element) { + foreach (['text', 'info'] as $string) { + $this->assertIsString( + $element[$string], + $assertMessage . ": {$string} of {$elementName} in {$key} should be a string" + ); + $this->assertNotEmpty( + $element[$string], + $assertMessage . ": {$string} of {$elementName} in {$key} should not be empty" + ); + } + } + } + } + }) + ); + } + + public function provideGetDwDescripter() { + return [ + ['pub', 'empty_token'], + ['pub', 'malformed_token'], + ['usr', 'invalid_token'], + ['usr', 'expired_token'], + ['usr', 'revoked_token'], + ['usr', 'valid_token'] + ]; + } } diff --git a/tests/integration/lib/Rest/WarehouseExportControllerProviderTest.php b/tests/integration/lib/Rest/WarehouseExportControllerProviderTest.php index 9aef32bde6..e2bf6652d1 100644 --- a/tests/integration/lib/Rest/WarehouseExportControllerProviderTest.php +++ b/tests/integration/lib/Rest/WarehouseExportControllerProviderTest.php @@ -200,7 +200,8 @@ public function testGetRealmsTokenAuth($role, $tokenType) { 'params' => null, 'data' => null, 'endpoint_type' => 'rest', - 'authentication_type' => 'token_optional' + 'authentication_type' => 'token_optional', + 'want_public_user' => true ], parent::validateSuccessResponse(function ($body, $assertMessage) { $this->assertSame(3, $body['total'], $assertMessage); diff --git a/tests/integration/lib/TokenAuthTest.php b/tests/integration/lib/TokenAuthTest.php index 1eeae75ed0..6eda2e5e9f 100644 --- a/tests/integration/lib/TokenAuthTest.php +++ b/tests/integration/lib/TokenAuthTest.php @@ -150,7 +150,14 @@ public function runTokenAuthTest( ) ]; } elseif ('rest' === $input['endpoint_type']) { - $output = parent::validateAuthorizationErrorResponse(401); + // If token is empty and we want Public user, test that it returns a success response. + if (true === $input['want_public_user'] && 'empty_token' === $tokenType) { + $output = $this->validateSuccessResponse(function ($body, $assertMessage) { + parent::assertSame(true, $body['success'], $assertMessage); + }); + } else { + $output = parent::validateAuthorizationErrorResponse(401); + } } else { throw new Exception( "Unknown value for endpoint_type:"