diff --git a/.gitignore b/.gitignore index 7a9bebdf6..e90b4d509 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ public/apc.php .claude/ .nvmrc .codegraph +docs/ diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSponsorApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSponsorApiController.php index e99e12ba5..5ae4be982 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSponsorApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSponsorApiController.php @@ -3205,11 +3205,11 @@ public function deleteSocialNetwork($summit_id, $sponsor_id, $social_network_id) }); } - // Extra Questions + // Sponsor Extra Questions #[OA\Get( path: "/api/v1/summits/{id}/sponsors/{sponsor_id}/extra-questions", - description: "required-groups " . IGroup::SuperAdmins . ", " . IGroup::Administrators . ", " . IGroup::SummitAdministrators . ", " . IGroup::Sponsors, + description: "required-groups " . IGroup::SuperAdmins . ", " . IGroup::Administrators . ", " . IGroup::SummitAdministrators . ", " . IGroup::Sponsors . ", " . IGroup::SponsorExternalUsers, summary: 'Read Sponsor Extra Questions', operationId: 'getSponsorExtraQuestions', tags: ['Sponsors'], @@ -3219,6 +3219,7 @@ public function deleteSocialNetwork($summit_id, $sponsor_id, $social_network_id) IGroup::Administrators, IGroup::SummitAdministrators, IGroup::Sponsors, + IGroup::SponsorExternalUsers, ] ], security: [ @@ -3226,6 +3227,7 @@ public function deleteSocialNetwork($summit_id, $sponsor_id, $social_network_id) 'summit_sponsor_oauth2' => [ SummitScopes::ReadSummitData, SummitScopes::ReadAllSummitData, + SummitScopes::ReadSponsorExtraQuestions, ] ] ], @@ -3365,6 +3367,7 @@ function ($page, $per_page, $filter, $order, $applyExtraFilters) { 'summit_sponsor_oauth2' => [ SummitScopes::ReadSummitData, SummitScopes::ReadAllSummitData, + SummitScopes::ReadSponsorExtraQuestions, ] ] ], @@ -3421,6 +3424,7 @@ public function getMetadata($summit_id) [ 'summit_sponsor_oauth2' => [ SummitScopes::WriteSummitData, + SummitScopes::WriteSponsorExtraQuestions, ] ] ], @@ -3510,6 +3514,7 @@ public function addExtraQuestion($summit_id, $sponsor_id) 'summit_sponsor_oauth2' => [ SummitScopes::ReadSummitData, SummitScopes::ReadAllSummitData, + SummitScopes::ReadSponsorExtraQuestions, ] ] ], @@ -3600,6 +3605,7 @@ public function getExtraQuestion($summit_id, $sponsor_id, $extra_question_id) [ 'summit_sponsor_oauth2' => [ SummitScopes::WriteSummitData, + SummitScopes::WriteSponsorExtraQuestions, ] ] ], @@ -3695,6 +3701,7 @@ public function updateExtraQuestion($summit_id, $sponsor_id, $extra_question_id) [ 'summit_sponsor_oauth2' => [ SummitScopes::WriteSummitData, + SummitScopes::WriteSponsorExtraQuestions, ] ] ], @@ -3780,6 +3787,7 @@ public function deleteExtraQuestion($summit_id, $sponsor_id, $extra_question_id) [ 'summit_sponsor_oauth2' => [ SummitScopes::WriteSummitData, + SummitScopes::WriteSponsorExtraQuestions, ] ] ], @@ -3879,6 +3887,7 @@ function ($payload, $summit, $sponsor_id, $question_id) { [ 'summit_sponsor_oauth2' => [ SummitScopes::WriteSummitData, + SummitScopes::WriteSponsorExtraQuestions, ] ] ], @@ -3987,6 +3996,7 @@ function ($value_id, $payload, $summit, $sponsor_id, $extra_question_id) { [ 'summit_sponsor_oauth2' => [ SummitScopes::WriteSummitData, + SummitScopes::WriteSponsorExtraQuestions, ] ] ], diff --git a/app/Security/SummitScopes.php b/app/Security/SummitScopes.php index 75a71ab7d..04b4d21be 100644 --- a/app/Security/SummitScopes.php +++ b/app/Security/SummitScopes.php @@ -124,5 +124,8 @@ final class SummitScopes const WriteSummitsConfirmExternalOrders = SCOPE_BASE_REALM.'/summits/confirm-external-orders'; const ReadSummitsConfirmExternalOrders = SCOPE_BASE_REALM.'/summits/read-external-orders'; + + const WriteSponsorExtraQuestions = SCOPE_BASE_REALM.'/summits/sponsors/extra-questions/write'; + const ReadSponsorExtraQuestions = SCOPE_BASE_REALM.'/summits/sponsors/extra-questions/read'; } diff --git a/app/Swagger/Security/SponsorOAuth2Schema.php b/app/Swagger/Security/SponsorOAuth2Schema.php index 81f880857..5dbf82407 100644 --- a/app/Swagger/Security/SponsorOAuth2Schema.php +++ b/app/Swagger/Security/SponsorOAuth2Schema.php @@ -17,6 +17,8 @@ SummitScopes::ReadSummitData => 'Read Summit Sponsor Data', SummitScopes::ReadAllSummitData => 'Read All Summit Sponsor Data', SummitScopes::WriteSummitData => 'Write Summit Sponsor Data', + SummitScopes::ReadSponsorExtraQuestions => 'Read Summit Sponsor Extra Questions Data', + SummitScopes::WriteSponsorExtraQuestions => 'Write Summit Sponsor Extra Questions Data', ], ), ], diff --git a/database/migrations/config/APIEndpointsMigrationHelper.php b/database/migrations/config/APIEndpointsMigrationHelper.php new file mode 100644 index 000000000..3ded89412 --- /dev/null +++ b/database/migrations/config/APIEndpointsMigrationHelper.php @@ -0,0 +1,234 @@ +addSql($this->insertApiScope('summits', $scopeName, $desc, $desc)); + * $this->addSql($this->insertEndpointScope('summits', $endpointName, $scopeName)); + * $this->addSql($this->insertEndpointAuthzGroup('summits', $endpointName, $groupSlug)); + * } + * } + */ +trait APIEndpointsMigrationHelper +{ + /** + * Generate idempotent INSERT for api_endpoints table. + * + * @param string $apiName API identifier (e.g., 'summits') + * @param string $endpointName Endpoint identifier (e.g., 'get-sponsor-extra-questions') + * @param string $route Route pattern (e.g., '/api/v1/summits/{id}/sponsors/{sponsor_id}/extra-questions') + * @param string $httpMethod Plain HTTP method string (e.g., 'GET', 'POST', 'PUT', 'DELETE') + * @param bool $active Whether the endpoint is active (default: true) + * @param bool $allowCors Whether to allow CORS (default: true, matches seedApiEndpoints behavior) + * @param bool $allowCredentials Whether to allow credentials (default: true, matches seedApiEndpoints behavior) + * @return string SQL INSERT statement + */ + protected function insertEndpoint( + string $apiName, + string $endpointName, + string $route, + string $httpMethod, + bool $active = true, + bool $allowCors = true, + bool $allowCredentials = true + ): string { + $activeInt = $active ? 1 : 0; + $corsInt = $allowCors ? 1 : 0; + $credentialsInt = $allowCredentials ? 1 : 0; + + return <<addSql($this->insertApiScope( + self::API_NAME, + $readScope, + 'Read Summit Sponsor Extra Questions Data', + 'Read Summit Sponsor Extra Questions Data' + )); + $this->addSql($this->insertApiScope( + self::API_NAME, + $writeScope, + 'Write Summit Sponsor Extra Questions Data', + 'Write Summit Sponsor Extra Questions Data' + )); + + // 2. Insert endpoint_api_scopes associations + $associations = [ + ['get-sponsor-extra-questions', $readScope], + ['add-sponsor-extra-question', $writeScope], + ['get-sponsor-extra-question', $readScope], + ['update-sponsor-extra-question', $writeScope], + ['delete-sponsor-extra-question', $writeScope], + ['get-sponsor-extra-questions-metadata', $readScope], + ['add-sponsor-extra-question-value', $writeScope], + ['update-sponsor-extra-question-value', $writeScope], + ['delete-sponsor-extra-question-value', $writeScope], + ]; + + foreach ($associations as [$endpointName, $scopeName]) { + $this->addSql($this->insertEndpointScope(self::API_NAME, $endpointName, $scopeName)); + } + + // 3. Insert endpoint_api_authz_groups + $this->addSql($this->insertEndpointAuthzGroup(self::API_NAME, 'get-sponsor-extra-questions', $externalGroupSlug)); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema): void + { + $readScope = SummitScopes::ReadSponsorExtraQuestions; + $writeScope = SummitScopes::WriteSponsorExtraQuestions; + $externalGroupSlug = IGroup::SponsorExternalUsers; + + // Reverse order: authz groups → endpoint scopes → api scopes + $this->addSql($this->deleteEndpointAuthzGroup(self::API_NAME, 'get-sponsor-extra-questions', $externalGroupSlug)); + $this->addSql($this->deleteScopesEndpoints(self::API_NAME, [$readScope, $writeScope])); + $this->addSql($this->deleteApiScopes(self::API_NAME, [$readScope, $writeScope])); + } +} diff --git a/database/seeders/ApiEndpointsSeeder.php b/database/seeders/ApiEndpointsSeeder.php index feb38babe..78e1f35f9 100644 --- a/database/seeders/ApiEndpointsSeeder.php +++ b/database/seeders/ApiEndpointsSeeder.php @@ -2506,13 +2506,15 @@ private function seedSummitEndpoints() 'http_method' => 'GET', 'scopes' => [ SummitScopes::ReadSummitData, - SummitScopes::ReadAllSummitData + SummitScopes::ReadAllSummitData, + SummitScopes::ReadSponsorExtraQuestions, ], 'authz_groups' => [ IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators, IGroup::Sponsors, + IGroup::SponsorExternalUsers, ] ], [ @@ -2521,6 +2523,7 @@ private function seedSummitEndpoints() 'http_method' => 'POST', 'scopes' => [ SummitScopes::WriteSummitData, + SummitScopes::WriteSponsorExtraQuestions, ], 'authz_groups' => [ IGroup::SuperAdmins, @@ -2536,7 +2539,8 @@ private function seedSummitEndpoints() 'http_method' => 'GET', 'scopes' => [ SummitScopes::ReadSummitData, - SummitScopes::ReadAllSummitData + SummitScopes::ReadAllSummitData, + SummitScopes::ReadSponsorExtraQuestions, ], 'authz_groups' => [ IGroup::SuperAdmins, @@ -2552,6 +2556,7 @@ private function seedSummitEndpoints() 'http_method' => 'PUT', 'scopes' => [ SummitScopes::WriteSummitData, + SummitScopes::WriteSponsorExtraQuestions, ], 'authz_groups' => [ IGroup::SuperAdmins, @@ -2567,6 +2572,7 @@ private function seedSummitEndpoints() 'http_method' => 'DELETE', 'scopes' => [ SummitScopes::WriteSummitData, + SummitScopes::WriteSponsorExtraQuestions, ], 'authz_groups' => [ IGroup::SuperAdmins, @@ -2582,7 +2588,8 @@ private function seedSummitEndpoints() 'http_method' => 'GET', 'scopes' => [ SummitScopes::ReadSummitData, - SummitScopes::ReadAllSummitData + SummitScopes::ReadAllSummitData, + SummitScopes::ReadSponsorExtraQuestions, ], 'authz_groups' => [ IGroup::SuperAdmins, @@ -2598,6 +2605,7 @@ private function seedSummitEndpoints() 'http_method' => 'POST', 'scopes' => [ SummitScopes::WriteSummitData, + SummitScopes::WriteSponsorExtraQuestions, ], 'authz_groups' => [ IGroup::SuperAdmins, @@ -2613,6 +2621,7 @@ private function seedSummitEndpoints() 'http_method' => 'PUT', 'scopes' => [ SummitScopes::WriteSummitData, + SummitScopes::WriteSponsorExtraQuestions, ], 'authz_groups' => [ IGroup::SuperAdmins, @@ -2628,6 +2637,7 @@ private function seedSummitEndpoints() 'http_method' => 'DELETE', 'scopes' => [ SummitScopes::WriteSummitData, + SummitScopes::WriteSponsorExtraQuestions, ], 'authz_groups' => [ IGroup::SuperAdmins, diff --git a/database/seeders/ApiScopesSeeder.php b/database/seeders/ApiScopesSeeder.php index d6638f6b7..6941e8956 100644 --- a/database/seeders/ApiScopesSeeder.php +++ b/database/seeders/ApiScopesSeeder.php @@ -393,7 +393,18 @@ private function seedSummitScopes() 'name' => SummitScopes::WriteAttendeeNotesData, 'short_description' => 'Write Attendee Notes Data', 'description' => 'Grants write access for Attendee Notes Data', + ], + [ + 'name' => SummitScopes::ReadSponsorExtraQuestions, + 'short_description' => 'Read Summit Sponsor Extra Questions Data', + 'description' => 'Read Summit Sponsor Extra Questions Data', + ], + [ + 'name' => SummitScopes::WriteSponsorExtraQuestions, + 'short_description' => 'Write Summit Sponsor Extra Questions Data', + 'description' => 'Write Summit Sponsor Extra Questions Data', ] + ]; foreach ($scopes as $scope_info) { diff --git a/routes/api_v1.php b/routes/api_v1.php index 6c9d66b20..fa9ba05e7 100644 --- a/routes/api_v1.php +++ b/routes/api_v1.php @@ -1297,7 +1297,7 @@ }); }); - // extra questions + // sponsor extra questions Route::group(['prefix' => 'extra-questions'], function () { Route::get('', ['middleware' => 'auth.user', 'uses' => 'OAuth2SummitSponsorApiController@getExtraQuestions']); Route::post('', ['middleware' => 'auth.user', 'uses' => 'OAuth2SummitSponsorApiController@addExtraQuestion']);