diff --git a/Libs/ModelSerializers/AbstractSerializer.php b/Libs/ModelSerializers/AbstractSerializer.php index db40514e..a9dea4d0 100644 --- a/Libs/ModelSerializers/AbstractSerializer.php +++ b/Libs/ModelSerializers/AbstractSerializer.php @@ -123,6 +123,35 @@ abstract class AbstractSerializer implements IModelSerializer return array_merge(array($class_name), $parents); } + const BoolType = 'json_boolean'; + const EpochType = 'datetime_epoch'; + const StringType = 'json_string'; + const IntType = 'json_int'; + const FloatType = 'json_float'; + const ObfuscatedEmailType = 'json_obfuscated_email'; + const UrlType = 'json_url'; + + const ValidTypes = [ + self::BoolType, + self::EpochType, + self::StringType, + self::IntType, + self::FloatType, + self::ObfuscatedEmailType, + self::UrlType, + ]; + + /** + * @param string $field + * @param string $type + * @return string + */ + public static function buildMapping(string $field, string $type):string { + if(!in_array($type, self::ValidTypes)) + throw new \InvalidArgumentException(); + return sprintf("%s:%s", $field, $type); + } + /** * @param null $expand * @param array $fields diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSpeakersApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSpeakersApiController.php index d43f1eb2..23a3aa96 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSpeakersApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSpeakersApiController.php @@ -31,6 +31,8 @@ use ModelSerializers\ISerializerTypeSelector; use ModelSerializers\SerializerRegistry; use services\model\ISpeakerService; use services\model\ISummitService; +use utils\Filter; +use utils\FilterElement; use utils\FilterParser; use utils\OrderParser; use utils\PagingInfo; @@ -125,81 +127,68 @@ final class OAuth2SummitSpeakersApiController extends OAuth2ProtectedController * Speakers endpoints */ + use ParametrizedGetAll; + /** * @param $summit_id * @return mixed */ public function getSpeakers($summit_id) { - try { - $summit = SummitFinderStrategyFactory::build($this->repository, $this->resource_server_context)->find($summit_id); - if (is_null($summit)) return $this->error404(); - - $values = Input::all(); - - $rules = array - ( - 'page' => 'integer|min:1', - 'per_page' => 'required_with:page|integer|min:10|max:100', - ); - - $validation = Validator::make($values, $rules); - - if ($validation->fails()) { - $messages = $validation->messages()->toArray(); - - return $this->error412($messages); - } - - // default values - $page = 1; - $per_page = 10; - - if (Input::has('page')) { - $page = intval(Input::get('page')); - $per_page = intval(Input::get('per_page')); - } - - $filter = null; - - if (Input::has('filter')) { - $filter = FilterParser::parse(Input::get('filter'), [ + $summit = SummitFinderStrategyFactory::build($this->getRepository(), $this->getResourceServerContext())->find($summit_id); + if (is_null($summit)) return $this->error404(); + return $this->_getAll( + function(){ + return [ 'first_name' => ['=@', '=='], - 'last_name' => ['=@', '=='], - 'email' => ['=@', '=='], - 'id' => ['=='], - 'full_name' => ['=@', '=='], - ]); - } - - $order = null; - if (Input::has('order')) { - $order = OrderParser::parse(Input::get('order'), [ + 'last_name' => ['=@', '=='], + 'email' => ['=@', '=='], + 'id' => ['=='], + 'full_name' => ['=@', '=='], + ]; + }, + function(){ + return [ + 'first_name' => 'sometimes|string', + 'last_name' => 'sometimes|string', + 'email' => 'sometimes|string', + 'id' => 'sometimes|integer', + 'full_name' => 'sometimes|string', + ]; + }, + function() + { + return [ 'first_name', 'last_name', 'id', 'email', - ]); - } - - $serializer_type = $this->serializer_type_selector->getSerializerType(); - $result = $this->speaker_repository->getSpeakersBySummit($summit, new PagingInfo($page, $per_page), $filter, $order); - - return $this->ok - ( - $result->toArray(Request::input('expand', ''), [], [], ['summit_id' => $summit_id, 'published' => true, 'summit' => $summit], $serializer_type) - ); - } catch (ValidationException $ex1) { - Log::warning($ex1); - return $this->error412($ex1->getMessages()); - } catch (EntityNotFoundException $ex2) { - Log::warning($ex2); - return $this->error404(array('message' => $ex2->getMessage())); - } catch (Exception $ex) { - Log::error($ex); - return $this->error500($ex); - } + ]; + }, + function($filter) use($summit){ + return $filter; + }, + function(){ + return $this->serializer_type_selector->getSerializerType(); + }, + null, + null, + function ($page, $per_page, $filter, $order, $applyExtraFilters) use($summit) { + return $this->speaker_repository->getSpeakersBySummit + ( + $summit, + new PagingInfo($page, $per_page), + call_user_func($applyExtraFilters, $filter), + $order + ); + }, + [ + 'summit_id' => $summit_id, + 'published' => true, + 'summit' => $summit + ] + ); } /** @@ -208,71 +197,51 @@ final class OAuth2SummitSpeakersApiController extends OAuth2ProtectedController */ public function getAll() { - try { - - $values = Input::all(); - $serializer_type = $this->serializer_type_selector->getSerializerType(); - $rules = [ - 'page' => 'integer|min:1', - 'per_page' => 'required_with:page|integer|min:10|max:100', - ]; - - $validation = Validator::make($values, $rules); - - if ($validation->fails()) { - $messages = $validation->messages()->toArray(); - - return $this->error412($messages); - } - - // default values - $page = 1; - $per_page = 10; - - if (Input::has('page')) { - $page = intval(Input::get('page')); - $per_page = intval(Input::get('per_page')); - } - - $filter = null; - - if (Input::has('filter')) { - $filter = FilterParser::parse(Input::get('filter'), [ - + return $this->_getAll( + function(){ + return [ 'first_name' => ['=@', '=='], - 'last_name' => ['=@', '=='], - 'full_name' => ['=@', '=='], - 'email' => ['=@', '=='], - 'id' => ['=='], - ]); - } - - $order = null; - if (Input::has('order')) { - $order = OrderParser::parse(Input::get('order'), [ - 'id', - 'email', + 'last_name' => ['=@', '=='], + 'email' => ['=@', '=='], + 'id' => ['=='], + 'full_name' => ['=@', '=='], + ]; + }, + function(){ + return [ + 'first_name' => 'sometimes|string', + 'last_name' => 'sometimes|string', + 'email' => 'sometimes|string', + 'id' => 'sometimes|integer', + 'full_name' => 'sometimes|string', + ]; + }, + function() + { + return [ 'first_name', 'last_name', - ]); + 'id', + 'email', + ]; + }, + function($filter){ + return $filter; + }, + function(){ + return $this->serializer_type_selector->getSerializerType(); + }, + null, + null, + function ($page, $per_page, $filter, $order, $applyExtraFilters) { + return $this->speaker_repository->getAllByPage + ( + new PagingInfo($page, $per_page), + call_user_func($applyExtraFilters, $filter), + $order + ); } - - $result = $this->speaker_repository->getAllByPage(new PagingInfo($page, $per_page), $filter, $order); - - return $this->ok - ( - $result->toArray(Request::input('expand', ''), [], [], [], $serializer_type) - ); - } catch (ValidationException $ex1) { - Log::warning($ex1); - return $this->error412($ex1->getMessages()); - } catch (EntityNotFoundException $ex2) { - Log::warning($ex2); - return $this->error404(array('message' => $ex2->getMessage())); - } catch (Exception $ex) { - Log::error($ex); - return $this->error500($ex); - } + ); } /** diff --git a/app/Http/Controllers/ConfigurationsController.php b/app/Http/Controllers/ConfigurationsController.php new file mode 100644 index 00000000..8fda92de --- /dev/null +++ b/app/Http/Controllers/ConfigurationsController.php @@ -0,0 +1,79 @@ +repository = $repository; + } + + /** + * @return \Illuminate\Http\JsonResponse|mixed + */ + public function getEndpointsDefinitions(){ + try { + $items = []; + foreach ($this->repository->getAll() as $i) { + if ($i instanceof IEntity) { + $i = SerializerRegistry::getInstance()->getSerializer($i, SerializerRegistry::SerializerType_Public)->serialize(Input::get('expand', '')); + } + $items[] = $i; + } + + $routeCollection = Route::getRoutes(); + + $public_endpoints = []; + foreach ($routeCollection as $value) { + $uri = $value->uri; + if(!str_contains($uri, 'api/public/v1')) continue; + $public_endpoints[] = [ + 'route' => $uri, + 'http_methods' => $value->methods, + ]; + } + + return $this->ok( + [ + 'oauth2_endpoints' => $items, + 'public_endpoints' => $public_endpoints, + ] + ); + } + catch (Exception $ex) { + Log::error($ex); + return $this->error500($ex); + } + } +} \ No newline at end of file diff --git a/app/Http/Routes/public.php b/app/Http/Routes/public.php index 2553f1d4..4cfb26a8 100644 --- a/app/Http/Routes/public.php +++ b/app/Http/Routes/public.php @@ -109,6 +109,16 @@ Route::group([ Route::get('sent', 'OAuth2SummitNotificationsApiController@getAllApprovedByUser'); }); + // speakers + + // speakers + Route::group(['prefix' => 'speakers'], function () { + Route::get('', 'OAuth2SummitSpeakersApiController@getSpeakers'); + Route::group(['prefix' => '{speaker_id}'], function () { + Route::get('', 'OAuth2SummitSpeakersApiController@getSummitSpeaker')->where('speaker_id', '[0-9]+'); + }); + }); + }); }); @@ -151,3 +161,17 @@ Route::group([ }); }); + + +Route::group([ + 'namespace' => 'App\Http\Controllers', + 'prefix' => '.well-known', + 'before' => [], + 'after' => [], + 'middleware' => [ + 'ssl', + 'rate.limit:1000,1', // 1000 request per minute + ] +], function(){ + Route::get('endpoints', 'ConfigurationsController@getEndpointsDefinitions'); +}); \ No newline at end of file diff --git a/app/ModelSerializers/ResourceServer/ApiEndpointAuthzGroupSerializer.php b/app/ModelSerializers/ResourceServer/ApiEndpointAuthzGroupSerializer.php new file mode 100644 index 00000000..4530b9bf --- /dev/null +++ b/app/ModelSerializers/ResourceServer/ApiEndpointAuthzGroupSerializer.php @@ -0,0 +1,25 @@ + 'id:json_int', + 'Slug' => 'slug:json_string', + ]; +} \ No newline at end of file diff --git a/app/ModelSerializers/ResourceServer/ApiEndpointSerializer.php b/app/ModelSerializers/ResourceServer/ApiEndpointSerializer.php new file mode 100644 index 00000000..b4fdb7cc --- /dev/null +++ b/app/ModelSerializers/ResourceServer/ApiEndpointSerializer.php @@ -0,0 +1,92 @@ + 'id:json_int', + 'Name' => 'name:json_string', + 'Description' => 'description:json_string', + 'Active' => 'active:json_boolean', + 'HttpMethod' => 'http_method:json_string', + 'Route' => 'route:json_string', + ]; + + protected static $allowed_relations = [ + 'scopes', + 'authz_groups', + ]; + + /** + * @param null $expand + * @param array $fields + * @param array $relations + * @param array $params + * @return array + */ + public function serialize($expand = null, array $fields = array(), array $relations = array(), array $params = array()) + { + $endpoint = $this->object; + if(!$endpoint instanceof ApiEndpoint) return []; + + if(!count($relations)) $relations = $this->getAllowedRelations(); + + $values = parent::serialize($expand, $fields, $relations, $params); + + if(in_array('scopes', $relations)) + $values['scopes'] = $endpoint->getScopeIds(); + + if(in_array('authz_groups', $relations)) + $values['authz_groups'] = $endpoint->getAuthGroupIds(); + + if (!empty($expand)) { + foreach (explode(',', $expand) as $relation) { + $relation = trim($relation); + switch (trim($relation)) { + + case 'scopes': { + if(!in_array('scopes', $relations)) break; + $scopes = []; + unset($values['scopes']); + foreach ($endpoint->getScopes() as $e) { + $scopes[] = SerializerRegistry::getInstance()->getSerializer($e)->serialize(AbstractSerializer::filterExpandByPrefix($expand, $relation)); + } + $values['scopes'] = $scopes; + } + break; + case 'authz_groups': { + if(!in_array('authz_groups', $relations)) break; + $authz_groups = []; + unset($values['authz_groups']); + foreach ($endpoint->getAuthzGroups() as $e) { + $authz_groups[] = SerializerRegistry::getInstance()->getSerializer($e)->serialize(AbstractSerializer::filterExpandByPrefix($expand, $relation)); + } + $values['authz_groups'] = $authz_groups; + } + break; + + } + } + } + return $values; + } +} \ No newline at end of file diff --git a/app/ModelSerializers/ResourceServer/ApiScopeSerializer.php b/app/ModelSerializers/ResourceServer/ApiScopeSerializer.php new file mode 100644 index 00000000..6c9eb261 --- /dev/null +++ b/app/ModelSerializers/ResourceServer/ApiScopeSerializer.php @@ -0,0 +1,27 @@ + 'id:json_int', + 'Name' => 'name:json_string', + 'Description' => 'description:json_string', + 'Active' => 'active:json_boolean', + ]; +} \ No newline at end of file diff --git a/app/ModelSerializers/ResourceServer/ApiSerializer.php b/app/ModelSerializers/ResourceServer/ApiSerializer.php new file mode 100644 index 00000000..36f91e6a --- /dev/null +++ b/app/ModelSerializers/ResourceServer/ApiSerializer.php @@ -0,0 +1,90 @@ + 'id:json_int', + 'Name' => 'name:json_string', + 'Description' => 'description:json_string', + 'Active' => 'active:json_boolean', + ]; + + protected static $allowed_relations = [ + 'scopes', + 'endpoints', + ]; + + /** + * @param null $expand + * @param array $fields + * @param array $relations + * @param array $params + * @return array + */ + public function serialize($expand = null, array $fields = array(), array $relations = array(), array $params = array()) + { + $api = $this->object; + if(!$api instanceof Api) return []; + + if(!count($relations)) $relations = $this->getAllowedRelations(); + + $values = parent::serialize($expand, $fields, $relations, $params); + + if(in_array('scopes', $relations)) + $values['scopes'] = $api->getScopeIds(); + + if(in_array('endpoints', $relations)) + $values['endpoints'] = $api->getEndpointsIds(); + + if (!empty($expand)) { + foreach (explode(',', $expand) as $relation) { + $relation = trim($relation); + switch (trim($relation)) { + case 'scopes': { + if(!in_array('scopes', $relations)) break; + $scopes = []; + unset($values['scopes']); + foreach ($api->getScopes() as $s) { + $scopes[] = SerializerRegistry::getInstance()->getSerializer($s)->serialize(AbstractSerializer::filterExpandByPrefix($expand, $relation)); + } + $values['scopes'] = $scopes; + } + break; + case 'endpoints': { + if(!in_array('endpoints', $relations)) break; + $endpoints = []; + unset($values['endpoints']); + foreach ($api->getEndpoints() as $e) { + $endpoints[] = SerializerRegistry::getInstance()->getSerializer($e)->serialize(AbstractSerializer::filterExpandByPrefix($expand, $relation)); + } + $values['endpoints'] = $endpoints; + } + break; + + } + } + } + return $values; + } +} + diff --git a/app/ModelSerializers/SerializerRegistry.php b/app/ModelSerializers/SerializerRegistry.php index c662a3c3..4a275459 100644 --- a/app/ModelSerializers/SerializerRegistry.php +++ b/app/ModelSerializers/SerializerRegistry.php @@ -42,6 +42,10 @@ use App\ModelSerializers\Marketplace\ServiceOfferedTypeSerializer; use App\ModelSerializers\Marketplace\SpokenLanguageSerializer; use App\ModelSerializers\Marketplace\SupportChannelTypeSerializer; use App\ModelSerializers\PushNotificationMessageSerializer; +use App\ModelSerializers\ResourceServer\ApiEndpointAuthzGroupSerializer; +use App\ModelSerializers\ResourceServer\ApiEndpointSerializer; +use App\ModelSerializers\ResourceServer\ApiScopeSerializer; +use App\ModelSerializers\ResourceServer\ApiSerializer; use App\ModelSerializers\Software\OpenStackComponentSerializer; use App\ModelSerializers\Software\OpenStackReleaseSerializer; use App\ModelSerializers\Summit\AdminSummitSerializer; @@ -115,6 +119,12 @@ final class SerializerRegistry private function __construct() { $this->resource_server_context = App::make(IResourceServerContext::class); + // resource server config + $this->registry['Api'] = ApiSerializer::class; + $this->registry['ApiEndpoint'] = ApiEndpointSerializer::class; + $this->registry['ApiScope'] = ApiScopeSerializer::class; + $this->registry['ApiEndpointAuthzGroup'] = ApiEndpointAuthzGroupSerializer::class; + // $this->registry['Summit'] = [ self::SerializerType_Public => SummitSerializer::class, diff --git a/app/Models/Foundation/Main/Member.php b/app/Models/Foundation/Main/Member.php index 6b3f9b14..cd2f2d12 100644 --- a/app/Models/Foundation/Main/Member.php +++ b/app/Models/Foundation/Main/Member.php @@ -715,7 +715,7 @@ class Member extends SilverstripeBaseModel } public function getCCLATeamsIds(){ - $ids = []; + $ids = []; foreach ($this->getCCLATeams() as $t) { $ids[] = intval($t->getId()); } diff --git a/app/Models/Foundation/Summit/Speakers/PresentationSpeaker.php b/app/Models/Foundation/Summit/Speakers/PresentationSpeaker.php index e2638628..28d1d1bf 100644 --- a/app/Models/Foundation/Summit/Speakers/PresentationSpeaker.php +++ b/app/Models/Foundation/Summit/Speakers/PresentationSpeaker.php @@ -909,9 +909,11 @@ class PresentationSpeaker extends SilverstripeBaseModel */ public function getPresentationIds($summit_id, $published_ones = true) { - return $this->presentations($summit_id, $published_ones)->map(function($entity) { - return $entity->getId(); - })->toArray(); + $ids = []; + foreach ($this->presentations($summit_id, $published_ones) as $p) { + $ids[] = intval($p->getId()); + } + return $ids; } /** @@ -920,9 +922,11 @@ class PresentationSpeaker extends SilverstripeBaseModel */ public function getAllPresentationIds($published_ones = true) { - return $this->presentations(null, $published_ones)->map(function($entity) { - return $entity->getId(); - })->toArray(); + $ids = []; + foreach ($this->presentations(null, $published_ones) as $p) { + $ids[] = intval($p->getId()); + } + return $ids; } /** @@ -956,9 +960,11 @@ class PresentationSpeaker extends SilverstripeBaseModel */ public function getModeratedPresentationIds($summit_id, $published_ones = true) { - return $this->moderated_presentations($summit_id, $published_ones)->map(function($entity) { - return $entity->getId(); - })->toArray(); + $ids = []; + foreach ($this->moderated_presentations($summit_id, $published_ones) as $p) { + $ids[] = intval($p->getId()); + } + return $ids; } /** @@ -967,9 +973,11 @@ class PresentationSpeaker extends SilverstripeBaseModel */ public function getAllModeratedPresentationIds($published_ones = true) { - return $this->moderated_presentations(null, $published_ones)->map(function($entity) { - return $entity->getId(); - })->toArray(); + $ids = []; + foreach ($this->moderated_presentations(null, $published_ones) as $p) { + $ids[] = intval($p->getId()); + } + return $ids; } /** diff --git a/app/Models/ResourceServer/Api.php b/app/Models/ResourceServer/Api.php index 503f4098..259a1301 100644 --- a/app/Models/ResourceServer/Api.php +++ b/app/Models/ResourceServer/Api.php @@ -11,12 +11,10 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ - use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Mapping AS ORM; - /** - * @ORM\Entity + * @ORM\Entity(repositoryClass="repositories\resource_server\DoctrineApiRepository") * @ORM\Table(name="apis") * @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="resource_server_region") * Class Api @@ -161,4 +159,25 @@ class Api extends ResourceServerEntity implements IApi return $scope; } + /** + * @return int[] + */ + public function getScopeIds():array { + $ids = []; + foreach ($this->getScopes() as $e) { + $ids[] = intval($e->getId()); + } + return $ids; + } + + /** + * @return int[] + */ + public function getEndpointsIds():array { + $ids = []; + foreach ($this->getEndpoints() as $e) { + $ids[] = intval($e->getId()); + } + return $ids; + } } \ No newline at end of file diff --git a/app/Models/ResourceServer/ApiEndpoint.php b/app/Models/ResourceServer/ApiEndpoint.php index 058941b0..1deae801 100644 --- a/app/Models/ResourceServer/ApiEndpoint.php +++ b/app/Models/ResourceServer/ApiEndpoint.php @@ -343,4 +343,25 @@ class ApiEndpoint extends ResourceServerEntity implements IApiEndpoint } + /** + * @return int[] + */ + public function getScopeIds():array { + $ids = []; + foreach ($this->getScopes() as $e) { + $ids[] = intval($e->getId()); + } + return $ids; + } + + /** + * @return int[] + */ + public function getAuthGroupIds():array { + $ids = []; + foreach ($this->getAuthzGroups() as $e) { + $ids[] = intval($e->getId()); + } + return $ids; + } } \ No newline at end of file diff --git a/app/Models/ResourceServer/IApiRepository.php b/app/Models/ResourceServer/IApiRepository.php new file mode 100644 index 00000000..5b934ac3 --- /dev/null +++ b/app/Models/ResourceServer/IApiRepository.php @@ -0,0 +1,22 @@ +