diff --git a/app/config/server.php b/app/config/server.php index abb900c2..6e2fa4e0 100644 --- a/app/config/server.php +++ b/app/config/server.php @@ -42,6 +42,7 @@ return array( 'OAuth2_AuthorizationCode_Lifetime' => 240, 'OAuth2_AccessToken_Lifetime' => 3600, 'OAuth2_RefreshToken_Lifetime' => 0, + 'OAuth2_Enable' => true, //oauth2 security policy configuration 'OAuth2SecurityPolicy_MinutesWithoutExceptions' => 2, 'OAuth2SecurityPolicy_MaxBearerTokenDisclosureAttempts' => 5, diff --git a/app/controllers/apis/ApiEndpointController.php b/app/controllers/apis/ApiEndpointController.php index 0c01250e..9c66dafc 100644 --- a/app/controllers/apis/ApiEndpointController.php +++ b/app/controllers/apis/ApiEndpointController.php @@ -75,6 +75,7 @@ class ApiEndpointController extends AbstractRESTController implements ICRUDContr 'route' => 'required|route', 'http_method' => 'required|httpmethod', 'api_id' => 'required|integer', + 'rate_limit' => 'required|integer', ); // Creates a Validator instance and validates the data. @@ -92,7 +93,8 @@ class ApiEndpointController extends AbstractRESTController implements ICRUDContr $new_api_endpoint['allow_cors'], $new_api_endpoint['route'], $new_api_endpoint['http_method'], - $new_api_endpoint['api_id'] + $new_api_endpoint['api_id'], + $new_api_endpoint['rate_limit'] ); return $this->created(array('api_endpoint_id' => $new_api_endpoint_model->id)); } @@ -135,6 +137,7 @@ class ApiEndpointController extends AbstractRESTController implements ICRUDContr 'allow_cors' => 'sometimes|required|boolean', 'route' => 'sometimes|required|route', 'http_method' => 'sometimes|required|httpmethod', + 'rate_limit' => 'required|integer', ); // Creates a Validator instance and validates the data. diff --git a/app/controllers/apis/JsonController.php b/app/controllers/apis/JsonController.php index 5063046a..807d2cf2 100644 --- a/app/controllers/apis/JsonController.php +++ b/app/controllers/apis/JsonController.php @@ -16,26 +16,57 @@ abstract class JsonController extends BaseController { protected function error500(Exception $ex){ $this->log_service->error($ex); - return Response::json(array('error' => 'server error'), 500); + return Response::json(array('message' => 'server error'), 500); } protected function created($data='ok'){ - return Response::json($data, 201); + $res = Response::json($data, 201); + //jsonp + if(Input::has('callback')) + $res->setCallback(Input::get('callback')); + return $res; } protected function deleted($data='ok'){ - return Response::json($data, 204); + $res = Response::json($data, 204); + //jsonp + if(Input::has('callback')) + $res->setCallback(Input::get('callback')); + return $res; } protected function ok($data='ok'){ - return Response::json($data, 200); + $res = Response::json($data, 200); + //jsonp + if(Input::has('callback')) + $res->setCallback(Input::get('callback')); + return $res; } protected function error400($data){ return Response::json($data, 400); } - protected function error404($data){ + protected function error404($data = array('message' => 'Entity Not Found')){ return Response::json($data, 404); } + + /** + * { + "message": "Validation Failed", + "errors": [ + { + "resource": "Issue", + "field": "title", + "code": "missing_field" + } + ] + } + * @param $messages + * @return mixed + */ + protected function error412($messages){ + + return Response::json(array('message' => 'Validation Failed', 'errors' => $messages), 412); + } } \ No newline at end of file diff --git a/app/controllers/apis/protected/OAuth2ProtectedController.php b/app/controllers/apis/protected/OAuth2ProtectedController.php index 2b6df3f8..3e813285 100644 --- a/app/controllers/apis/protected/OAuth2ProtectedController.php +++ b/app/controllers/apis/protected/OAuth2ProtectedController.php @@ -11,6 +11,8 @@ abstract class OAuth2ProtectedController extends JsonController { protected $resource_server_context; + protected $repository; + public function __construct(IResourceServerContext $resource_server_context, ILogService $log_service) { parent::__construct($log_service); diff --git a/app/controllers/apis/protected/marketplace/OAuth2CloudApiController.php b/app/controllers/apis/protected/marketplace/OAuth2CloudApiController.php new file mode 100644 index 00000000..29ef0169 --- /dev/null +++ b/app/controllers/apis/protected/marketplace/OAuth2CloudApiController.php @@ -0,0 +1,81 @@ +getCompanyServices(); + } + + /** + * @param $id + * @return mixed + */ + public function getCloud($id) + { + return $this->getCompanyService($id); + } + + /** + * @param $id + * @return mixed + */ + public function getCloudDataCenters($id) + { + try{ + $cloud = $this->repository->getById($id); + + if(!$cloud) + return $this->error404(); + + $data_center_regions = $cloud->datacenters_regions(); + $res = array(); + + foreach($data_center_regions as $region){ + $data = $region->toArray(); + $locations = $region->locations(); + $data_locations = array(); + foreach($locations as $loc){ + array_push($data_locations, $loc->toArray()); + } + $data['locations'] = $data_locations; + array_push($res, $data); + } + + return $this->ok(array('datacenters' => $res )); + } + catch(Exception $ex){ + $this->log_service->error($ex); + return $this->error500($ex); + } + } +} \ No newline at end of file diff --git a/app/controllers/apis/protected/marketplace/OAuth2CompanyServiceApiController.php b/app/controllers/apis/protected/marketplace/OAuth2CompanyServiceApiController.php new file mode 100644 index 00000000..46813d96 --- /dev/null +++ b/app/controllers/apis/protected/marketplace/OAuth2CompanyServiceApiController.php @@ -0,0 +1,133 @@ + 'The :attribute field is does not has a valid value (all, active, non_active).', + 'order' => 'The :attribute field is does not has a valid value (date, name).', + 'order_dir' => 'The :attribute field is does not has a valid value (desc, asc).', + ); + + $rules = array( + 'page' => 'integer|min:1', + 'per_page' => 'required_with:page|integer|min:10|max:100', + 'status' => 'status', + 'order_by' => 'order', + 'order_dir' => 'required_with:order_by|order_dir', + ); + // Creates a Validator instance and validates the data. + $validation = Validator::make($values, $rules, $messages); + + if ($validation->fails()) { + $messages = $validation->messages()->toArray(); + return $this->error412($messages); + } + + if(Input::has('page')){ + $page = intval(Input::get('page')); + $per_page = intval(Input::get('per_page')); + } + + if(Input::has('status')){ + $status = Input::get('status'); + } + + if(Input::has('order_by')){ + $order_by = Input::get('order_by'); + $order_dir = Input::get('order_dir'); + } + + $data = $this->repository->getAll($page, $per_page, $status, $order_by, $order_dir); + return $this->ok($data); + } + catch(Exception $ex){ + $this->log_service->error($ex); + return $this->error500($ex); + } + } + + /** + * @param $id + * @return mixed + */ + public function getCompanyService($id) + { + try{ + $data = $this->repository->getById($id); + return ($data)? $this->ok($data) : $this->error404(); + } + catch(Exception $ex){ + $this->log_service->error($ex); + return $this->error500($ex); + } + } +} \ No newline at end of file diff --git a/app/controllers/apis/protected/marketplace/OAuth2ConsultantsApiController.php b/app/controllers/apis/protected/marketplace/OAuth2ConsultantsApiController.php new file mode 100644 index 00000000..a5162e8d --- /dev/null +++ b/app/controllers/apis/protected/marketplace/OAuth2ConsultantsApiController.php @@ -0,0 +1,82 @@ +repository = $repository; + parent::__construct($resource_server_context,$log_service); + } + + /** + * query string params: + * page: You can specify further pages + * per_page: custom page size up to 100 ( min 10) + * status: cloud status ( active , not active, all) + * order_by: order by field + * order_dir: order direction + * @return mixed + */ + public function getConsultants() + { + return $this->getCompanyServices(); + } + + /** + * @param $id + * @return mixed + */ + public function getConsultant($id) + { + return $this->getCompanyService($id); + } + + /** + * @param $id + * @return mixed + */ + public function getOffices($id) + { + try{ + $consultant = $this->repository->getById($id); + + if(!$consultant) + return $this->error404(); + + $offices = $consultant->offices(); + $res = array(); + + foreach($offices as $office){ + array_push($res, $office->toArray()); + } + return $this->ok(array('offices' => $res)); + } + catch(Exception $ex){ + $this->log_service->error($ex); + return $this->error500($ex); + } + } +} \ No newline at end of file diff --git a/app/controllers/apis/protected/marketplace/OAuth2PrivateCloudApiController.php b/app/controllers/apis/protected/marketplace/OAuth2PrivateCloudApiController.php new file mode 100644 index 00000000..727bed68 --- /dev/null +++ b/app/controllers/apis/protected/marketplace/OAuth2PrivateCloudApiController.php @@ -0,0 +1,27 @@ +repository = $repository; + } +} \ No newline at end of file diff --git a/app/controllers/apis/protected/marketplace/OAuth2PublicCloudApiController.php b/app/controllers/apis/protected/marketplace/OAuth2PublicCloudApiController.php new file mode 100644 index 00000000..56465062 --- /dev/null +++ b/app/controllers/apis/protected/marketplace/OAuth2PublicCloudApiController.php @@ -0,0 +1,29 @@ +repository = $repository; + } + +} \ No newline at end of file diff --git a/app/database/migrations/2015_03_17_153315_alter_table_oauth2_api_endpoint.php b/app/database/migrations/2015_03_17_153315_alter_table_oauth2_api_endpoint.php new file mode 100644 index 00000000..f1f4312e --- /dev/null +++ b/app/database/migrations/2015_03_17_153315_alter_table_oauth2_api_endpoint.php @@ -0,0 +1,35 @@ +bigInteger("rate_limit")->unsigned()->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + + Schema::table('oauth2_api_endpoint', function($table) + { + $table->dropColumn('rate_limit'); + }); + } + +} diff --git a/app/database/seeds/ApiEndpointSeeder.php b/app/database/seeds/ApiEndpointSeeder.php index 57084f2f..ead48802 100644 --- a/app/database/seeds/ApiEndpointSeeder.php +++ b/app/database/seeds/ApiEndpointSeeder.php @@ -1,4 +1,8 @@ delete(); DB::table('oauth2_api_endpoint')->delete(); $this->seedUsersEndpoints(); + $this->seedPublicCloudsEndpoints(); + $this->seedPrivateCloudsEndpoints(); + $this->seedConsultantsEndpoints(); } private function seedUsersEndpoints() @@ -24,8 +31,9 @@ class ApiEndpointSeeder extends Seeder 'http_method' => 'GET' ) ); + $profile_scope = ApiScope::where('name', '=', 'profile')->first(); - $email_scope = ApiScope::where('name', '=', 'email')->first(); + $email_scope = ApiScope::where('name', '=', 'email')->first(); $address_scope = ApiScope::where('name', '=', 'address')->first(); $get_user_info_endpoint = ApiEndpoint::where('name', '=', 'get-user-info')->first(); @@ -34,5 +42,146 @@ class ApiEndpointSeeder extends Seeder $get_user_info_endpoint->scopes()->attach($address_scope->id); } + private function seedPublicCloudsEndpoints(){ + $public_clouds = Api::where('name','=','public-clouds')->first(); + $current_realm = Config::get('app.url'); + // endpoints scopes + ApiEndpoint::create( + array( + 'name' => 'get-public-clouds', + 'active' => true, + 'api_id' => $public_clouds->id, + 'route' => '/api/v1/marketplace/public-clouds', + 'http_method' => 'GET' + ) + ); + + ApiEndpoint::create( + array( + 'name' => 'get-public-cloud', + 'active' => true, + 'api_id' => $public_clouds->id, + 'route' => '/api/v1/marketplace/public-clouds/{id}', + 'http_method' => 'GET' + ) + ); + + ApiEndpoint::create( + array( + 'name' => 'get-public-cloud-datacenters', + 'active' => true, + 'api_id' => $public_clouds->id, + 'route' => '/api/v1/marketplace/public-clouds/{id}/data-centers', + 'http_method' => 'GET' + ) + ); + + $public_cloud_read_scope = ApiScope::where('name','=',sprintf('%s/public-clouds/read',$current_realm))->first(); + + $endpoint_get_public_clouds = ApiEndpoint::where('name','=','get-public-clouds')->first(); + $endpoint_get_public_clouds->scopes()->attach($public_cloud_read_scope->id); + + $endpoint_get_public_cloud = ApiEndpoint::where('name','=','get-public-cloud')->first(); + $endpoint_get_public_cloud->scopes()->attach($public_cloud_read_scope->id); + + $endpoint_get_public_cloud_datacenters = ApiEndpoint::where('name','=','get-public-cloud-datacenters')->first(); + $endpoint_get_public_cloud_datacenters->scopes()->attach($public_cloud_read_scope->id); + } + + private function seedPrivateCloudsEndpoints(){ + $private_clouds = Api::where('name','=','private-clouds')->first(); + $current_realm = Config::get('app.url'); + // endpoints scopes + + ApiEndpoint::create( + array( + 'name' => 'get-private-clouds', + 'active' => true, + 'api_id' => $private_clouds->id, + 'route' => '/api/v1/marketplace/private-clouds', + 'http_method' => 'GET' + ) + ); + + ApiEndpoint::create( + array( + 'name' => 'get-private-cloud', + 'active' => true, + 'api_id' => $private_clouds->id, + 'route' => '/api/v1/marketplace/private-clouds/{id}', + 'http_method' => 'GET' + ) + ); + + ApiEndpoint::create( + array( + 'name' => 'get-private-cloud-datacenters', + 'active' => true, + 'api_id' => $private_clouds->id, + 'route' => '/api/v1/marketplace/private-clouds/{id}/data-centers', + 'http_method' => 'GET' + ) + ); + + $private_cloud_read_scope = ApiScope::where('name','=',sprintf('%s/private-clouds/read',$current_realm))->first(); + + $endpoint_get_private_clouds = ApiEndpoint::where('name','=','get-private-clouds')->first(); + $endpoint_get_private_clouds->scopes()->attach($private_cloud_read_scope->id); + + $endpoint_get_private_cloud = ApiEndpoint::where('name','=','get-private-cloud')->first(); + $endpoint_get_private_cloud->scopes()->attach($private_cloud_read_scope->id); + + $endpoint_get_private_cloud_datacenters = ApiEndpoint::where('name','=','get-private-cloud-datacenters')->first(); + $endpoint_get_private_cloud_datacenters->scopes()->attach($private_cloud_read_scope->id); + + } + + private function seedConsultantsEndpoints(){ + + $consultants = Api::where('name','=','consultants')->first(); + $current_realm = Config::get('app.url'); + // endpoints scopes + + ApiEndpoint::create( + array( + 'name' => 'get-consultants', + 'active' => true, + 'api_id' => $consultants->id, + 'route' => '/api/v1/marketplace/consultants', + 'http_method' => 'GET' + ) + ); + + ApiEndpoint::create( + array( + 'name' => 'get-consultant', + 'active' => true, + 'api_id' => $consultants->id, + 'route' => '/api/v1/marketplace/consultants/{id}', + 'http_method' => 'GET' + ) + ); + + ApiEndpoint::create( + array( + 'name' => 'get-consultant-offices', + 'active' => true, + 'api_id' => $consultants->id, + 'route' => '/api/v1/marketplace/consultants/{id}/offices', + 'http_method' => 'GET' + ) + ); + + $consultant_read_scope = ApiScope::where('name','=',sprintf('%s/consultants/read',$current_realm))->first(); + + $endpoint = ApiEndpoint::where('name','=','get-consultants')->first(); + $endpoint->scopes()->attach($consultant_read_scope->id); + + $endpoint = ApiEndpoint::where('name','=','get-consultant')->first(); + $endpoint->scopes()->attach($consultant_read_scope->id); + + $endpoint = ApiEndpoint::where('name','=','get-consultant-offices')->first(); + $endpoint->scopes()->attach($consultant_read_scope->id); + } } \ No newline at end of file diff --git a/app/database/seeds/ApiScopeSeeder.php b/app/database/seeds/ApiScopeSeeder.php index 4daf1067..0997669c 100644 --- a/app/database/seeds/ApiScopeSeeder.php +++ b/app/database/seeds/ApiScopeSeeder.php @@ -1,5 +1,8 @@ delete(); DB::table('oauth2_api_scope')->delete(); $this->seedUsersScopes(); + $this->seedPublicCloudScopes(); + $this->seedPrivateCloudScopes(); + $this->seedConsultantScopes(); } private function seedUsersScopes(){ @@ -46,4 +52,54 @@ class ApiScopeSeeder extends Seeder { ); } + + private function seedPublicCloudScopes(){ + + $current_realm = Config::get('app.url'); + $public_clouds = Api::where('name','=','public-clouds')->first(); + + ApiScope::create( + array( + 'name' => sprintf('%s/public-clouds/read',$current_realm), + 'short_description' => 'Get Public Clouds', + 'description' => 'Grants read only access for Public Clouds', + 'api_id' => $public_clouds->id, + 'system' => false, + ) + ); + } + + private function seedPrivateCloudScopes(){ + + $current_realm = Config::get('app.url'); + $private_clouds = Api::where('name','=','private-clouds')->first(); + + ApiScope::create( + array( + 'name' => sprintf('%s/private-clouds/read',$current_realm), + 'short_description' => 'Get Private Clouds', + 'description' => 'Grants read only access for Private Clouds', + 'api_id' => $private_clouds->id, + 'system' => false, + ) + ); + } + + + private function seedConsultantScopes(){ + + $current_realm = Config::get('app.url'); + $consultants = Api::where('name','=','consultants')->first(); + + ApiScope::create( + array( + 'name' => sprintf('%s/consultants/read',$current_realm), + 'short_description' => 'Get Consultants', + 'description' => 'Grants read only access for Consultants', + 'api_id' => $consultants->id, + 'system' => false, + ) + ); + } + } \ No newline at end of file diff --git a/app/database/seeds/ApiSeeder.php b/app/database/seeds/ApiSeeder.php index 96b437f0..7994c1f2 100644 --- a/app/database/seeds/ApiSeeder.php +++ b/app/database/seeds/ApiSeeder.php @@ -1,6 +1,10 @@ delete(); @@ -10,6 +14,7 @@ class ApiSeeder extends Seeder { $resource_server = ResourceServer::first(); + // users Api::create( array( 'name' => 'users', @@ -20,5 +25,38 @@ class ApiSeeder extends Seeder { 'logo' => asset('img/apis/server.png') ) ); + // public clouds + Api::create( + array( + 'name' => 'public-clouds', + 'logo' => null, + 'active' => true, + 'Description' => 'Marketplace Public Clouds', + 'resource_server_id' => $resource_server->id, + 'logo' => asset('img/apis/server.png') + ) + ); + // private clouds + Api::create( + array( + 'name' => 'private-clouds', + 'logo' => null, + 'active' => true, + 'Description' => 'Marketplace Private Clouds', + 'resource_server_id' => $resource_server->id, + 'logo' => asset('img/apis/server.png') + ) + ); + // consultants + Api::create( + array( + 'name' => 'consultants', + 'logo' => null, + 'active' => true, + 'Description' => 'Marketplace Consultants', + 'resource_server_id' => $resource_server->id, + 'logo' => asset('img/apis/server.png') + ) + ); } } \ No newline at end of file diff --git a/app/database/seeds/DatabaseSeeder.php b/app/database/seeds/DatabaseSeeder.php index 740862ee..57cf6920 100644 --- a/app/database/seeds/DatabaseSeeder.php +++ b/app/database/seeds/DatabaseSeeder.php @@ -1,5 +1,8 @@ seedApiEndpointScopes(); $this->seedApiScopeScopes(); $this->seedUsersScopes(); + $this->seedPublicCloudScopes(); + $this->seedPrivateCloudScopes(); + $this->seedConsultantScopes(); //endpoints $this->seedResourceServerEndpoints(); $this->seedApiEndpoints(); $this->seedApiEndpointEndpoints(); $this->seedScopeEndpoints(); $this->seedUsersEndpoints(); - + $this->seedPublicCloudsEndpoints(); + $this->seedPrivateCloudsEndpoints(); + $this->seedConsultantsEndpoints(); + //clients $this->seedTestUsersAndClients(); } @@ -474,6 +480,40 @@ class TestSeeder extends Seeder { 'logo' => asset('img/apis/server.png') ) ); + + Api::create( + array( + 'name' => 'public-clouds', + 'logo' => null, + 'active' => true, + 'Description' => 'Marketplace Public Clouds', + 'resource_server_id' => $resource_server->id, + 'logo' => asset('img/apis/server.png') + ) + ); + + Api::create( + array( + 'name' => 'private-clouds', + 'logo' => null, + 'active' => true, + 'Description' => 'Marketplace Private Clouds', + 'resource_server_id' => $resource_server->id, + 'logo' => asset('img/apis/server.png') + ) + ); + + Api::create( + array( + 'name' => 'consultants', + 'logo' => null, + 'active' => true, + 'Description' => 'Marketplace Consultants', + 'resource_server_id' => $resource_server->id, + 'logo' => asset('img/apis/server.png') + ) + ); + } private function seedResourceServerScopes(){ @@ -708,6 +748,7 @@ class TestSeeder extends Seeder { } private function seedApiScopeScopes(){ + $current_realm = Config::get('app.url'); $api_scope = Api::where('name','=','api-scope')->first(); @@ -806,7 +847,55 @@ class TestSeeder extends Seeder { 'system' => false, ) ); + } + private function seedPublicCloudScopes(){ + + $current_realm = Config::get('app.url'); + $public_clouds = Api::where('name','=','public-clouds')->first(); + + ApiScope::create( + array( + 'name' => sprintf('%s/public-clouds/read',$current_realm), + 'short_description' => 'Get Public Clouds', + 'description' => 'Get Public Clouds', + 'api_id' => $public_clouds->id, + 'system' => false, + ) + ); + } + + private function seedPrivateCloudScopes(){ + + $current_realm = Config::get('app.url'); + $private_clouds = Api::where('name','=','private-clouds')->first(); + + ApiScope::create( + array( + 'name' => sprintf('%s/private-clouds/read',$current_realm), + 'short_description' => 'Get Private Clouds', + 'description' => 'Get Private Clouds', + 'api_id' => $private_clouds->id, + 'system' => false, + ) + ); + } + + + private function seedConsultantScopes(){ + + $current_realm = Config::get('app.url'); + $consultants = Api::where('name','=','consultants')->first(); + + ApiScope::create( + array( + 'name' => sprintf('%s/consultants/read',$current_realm), + 'short_description' => 'Get Consultants', + 'description' => 'Get Consultants', + 'api_id' => $consultants->id, + 'system' => false, + ) + ); } private function seedResourceServerEndpoints(){ @@ -1273,4 +1362,147 @@ class TestSeeder extends Seeder { $get_user_info_endpoint->scopes()->attach($email_scope->id); $get_user_info_endpoint->scopes()->attach($address_scope->id); } + + private function seedPublicCloudsEndpoints(){ + $public_clouds = Api::where('name','=','public-clouds')->first(); + $current_realm = Config::get('app.url'); + // endpoints scopes + + ApiEndpoint::create( + array( + 'name' => 'get-public-clouds', + 'active' => true, + 'api_id' => $public_clouds->id, + 'route' => '/api/v1/marketplace/public-clouds', + 'http_method' => 'GET' + ) + ); + + ApiEndpoint::create( + array( + 'name' => 'get-public-cloud', + 'active' => true, + 'api_id' => $public_clouds->id, + 'route' => '/api/v1/marketplace/public-clouds/{id}', + 'http_method' => 'GET' + ) + ); + + ApiEndpoint::create( + array( + 'name' => 'get-public-cloud-datacenters', + 'active' => true, + 'api_id' => $public_clouds->id, + 'route' => '/api/v1/marketplace/public-clouds/{id}/data-centers', + 'http_method' => 'GET' + ) + ); + + $public_cloud_read_scope = ApiScope::where('name','=',sprintf('%s/public-clouds/read',$current_realm))->first(); + + $endpoint_get_public_clouds = ApiEndpoint::where('name','=','get-public-clouds')->first(); + $endpoint_get_public_clouds->scopes()->attach($public_cloud_read_scope->id); + + $endpoint_get_public_cloud = ApiEndpoint::where('name','=','get-public-cloud')->first(); + $endpoint_get_public_cloud->scopes()->attach($public_cloud_read_scope->id); + + $endpoint_get_public_cloud_datacenters = ApiEndpoint::where('name','=','get-public-cloud-datacenters')->first(); + $endpoint_get_public_cloud_datacenters->scopes()->attach($public_cloud_read_scope->id); + } + + private function seedPrivateCloudsEndpoints(){ + $private_clouds = Api::where('name','=','private-clouds')->first(); + $current_realm = Config::get('app.url'); + // endpoints scopes + + ApiEndpoint::create( + array( + 'name' => 'get-private-clouds', + 'active' => true, + 'api_id' => $private_clouds->id, + 'route' => '/api/v1/marketplace/private-clouds', + 'http_method' => 'GET' + ) + ); + + ApiEndpoint::create( + array( + 'name' => 'get-private-cloud', + 'active' => true, + 'api_id' => $private_clouds->id, + 'route' => '/api/v1/marketplace/private-clouds/{id}', + 'http_method' => 'GET' + ) + ); + + ApiEndpoint::create( + array( + 'name' => 'get-private-cloud-datacenters', + 'active' => true, + 'api_id' => $private_clouds->id, + 'route' => '/api/v1/marketplace/private-clouds/{id}/data-centers', + 'http_method' => 'GET' + ) + ); + + $private_cloud_read_scope = ApiScope::where('name','=',sprintf('%s/private-clouds/read',$current_realm))->first(); + + $endpoint_get_private_clouds = ApiEndpoint::where('name','=','get-private-clouds')->first(); + $endpoint_get_private_clouds->scopes()->attach($private_cloud_read_scope->id); + + $endpoint_get_private_cloud = ApiEndpoint::where('name','=','get-private-cloud')->first(); + $endpoint_get_private_cloud->scopes()->attach($private_cloud_read_scope->id); + + $endpoint_get_private_cloud_datacenters = ApiEndpoint::where('name','=','get-private-cloud-datacenters')->first(); + $endpoint_get_private_cloud_datacenters->scopes()->attach($private_cloud_read_scope->id); + + } + + private function seedConsultantsEndpoints(){ + + $consultants = Api::where('name','=','consultants')->first(); + $current_realm = Config::get('app.url'); + // endpoints scopes + + ApiEndpoint::create( + array( + 'name' => 'get-consultants', + 'active' => true, + 'api_id' => $consultants->id, + 'route' => '/api/v1/marketplace/consultants', + 'http_method' => 'GET' + ) + ); + + ApiEndpoint::create( + array( + 'name' => 'get-consultant', + 'active' => true, + 'api_id' => $consultants->id, + 'route' => '/api/v1/marketplace/consultants/{id}', + 'http_method' => 'GET' + ) + ); + + ApiEndpoint::create( + array( + 'name' => 'get-consultant-offices', + 'active' => true, + 'api_id' => $consultants->id, + 'route' => '/api/v1/marketplace/consultants/{id}/offices', + 'http_method' => 'GET' + ) + ); + + $consultant_read_scope = ApiScope::where('name','=',sprintf('%s/consultants/read',$current_realm))->first(); + + $endpoint = ApiEndpoint::where('name','=','get-consultants')->first(); + $endpoint->scopes()->attach($consultant_read_scope->id); + + $endpoint = ApiEndpoint::where('name','=','get-consultant')->first(); + $endpoint->scopes()->attach($consultant_read_scope->id); + + $endpoint = ApiEndpoint::where('name','=','get-consultant-offices')->first(); + $endpoint->scopes()->attach($consultant_read_scope->id); + } } \ No newline at end of file diff --git a/app/filters.php b/app/filters.php index 8217a01f..b0a4dc85 100644 --- a/app/filters.php +++ b/app/filters.php @@ -216,6 +216,12 @@ Route::filter('is.current.user',function($route, $request){ Route::filter('oauth2.protected.endpoint','OAuth2BearerAccessTokenRequestValidator'); +Route::filter('oauth2.rate.limiter','ApiEndpointRateLimiter'); + +Route::filter('oauth2.rate.limiter.headers','ApiEndpointRateLimiterHeaders'); + +Route::filter('oauth2.etag','ETagChecker'); + //oauth2 server admin filter Route::filter('oauth2.server.admin.json',function(){ diff --git a/app/filters/ApiEndpointRateLimiter.php b/app/filters/ApiEndpointRateLimiter.php new file mode 100644 index 00000000..de9f4d04 --- /dev/null +++ b/app/filters/ApiEndpointRateLimiter.php @@ -0,0 +1,84 @@ +api_endpoint_service = $api_endpoint_service; + $this->log_service = $log_service; + $this->checkpoint_service = $checkpoint_service; + $this->cache_service = $cache_service; + } + + /** + * @param $route + * @param $request + * @return mixed + */ + public function filter($route, $request) + { + $url = $route->getPath(); + if(strpos($url, '/') != 0){ + $url = '/'.$url; + } + $method = $request->getMethod(); + + try { + $endpoint = $this->api_endpoint_service->getApiEndpointByUrlAndMethod($url, $method); + if(!is_null($endpoint->rate_limit) && (int)$endpoint->rate_limit > 0){ + //do rate limit checking + $key = sprintf('rate.limit.%s_%s_%s',$url,$method,$request->getClientIp()); + $res = (int)$this->cache_service->getSingleValue($key); + if($res >= (int)$endpoint->rate_limit) + return Response::json(array('message' => "You have triggered an abuse detection mechanism and have been temporarily blocked from content creation. Please retry your request again later."), 403); + $this->cache_service->incCounter($key, (3600 * 60)); + } + } + catch(Exception $ex){ + $this->log_service->error($ex); + $this->checkpoint_service->trackException($ex); + } + } +} \ No newline at end of file diff --git a/app/filters/ApiEndpointRateLimiterHeaders.php b/app/filters/ApiEndpointRateLimiterHeaders.php new file mode 100644 index 00000000..bf44756b --- /dev/null +++ b/app/filters/ApiEndpointRateLimiterHeaders.php @@ -0,0 +1,87 @@ +api_endpoint_service = $api_endpoint_service; + $this->log_service = $log_service; + $this->checkpoint_service = $checkpoint_service; + $this->cache_service = $cache_service; + } + + /** + * @param $route + * @param $request + * @param $response + */ + public function filter($route, $request, $response) + { + $url = $route->getPath(); + if(strpos($url, '/') != 0){ + $url = '/'.$url; + } + $method = $request->getMethod(); + + try { + $endpoint = $this->api_endpoint_service->getApiEndpointByUrlAndMethod($url, $method); + if(!is_null($endpoint->rate_limit) && (int)$endpoint->rate_limit > 0){ + //do rate limit checking + $key = sprintf('rate.limit.%s_%s_%s',$url,$method,$request->getClientIp()); + $res = (int)$this->cache_service->getSingleValue($key); + if($res <= (int)$endpoint->rate_limit) + { + $response->headers->set('X-Ratelimit-Limit', $endpoint->rate_limit, false); + $response->headers->set('X-Ratelimit-Remaining', $endpoint->rate_limit-(int)$res, false); + $response->headers->set('X-RateLimit-Reset', $this->cache_service->ttl(($key)) , false); + } + } + } + catch(Exception $ex){ + $this->log_service->error($ex); + $this->checkpoint_service->trackException($ex); + } + } +} \ No newline at end of file diff --git a/app/filters/ETagChecker.php b/app/filters/ETagChecker.php new file mode 100644 index 00000000..951c9ec3 --- /dev/null +++ b/app/filters/ETagChecker.php @@ -0,0 +1,36 @@ +getStatusCode()!= 200) return; + $etag = md5($response->getContent()); + $requestETag = str_replace('"', '', $request->getETags()); + + if($requestETag && $requestETag[0] == $etag){ + $response->setNotModified(); + } + + $response->setEtag($etag); + } +} \ No newline at end of file diff --git a/app/libs/oauth2/services/IApiEndpointService.php b/app/libs/oauth2/services/IApiEndpointService.php index e0a766c3..50885ad6 100644 --- a/app/libs/oauth2/services/IApiEndpointService.php +++ b/app/libs/oauth2/services/IApiEndpointService.php @@ -48,9 +48,10 @@ interface IApiEndpointService { * @param string $route * @param string $http_method * @param int $api_id + * @param int $rate_limit * @return IApiEndpoint */ - public function add($name, $description, $active, $allow_cors, $route, $http_method, $api_id); + public function add($name, $description, $active, $allow_cors, $route, $http_method, $api_id, $rate_limit); /** diff --git a/app/libs/utils/model/BaseModelEloquent.php b/app/libs/utils/model/BaseModelEloquent.php index d0cad7d0..82e9ca32 100644 --- a/app/libs/utils/model/BaseModelEloquent.php +++ b/app/libs/utils/model/BaseModelEloquent.php @@ -2,13 +2,14 @@ namespace utils\model; use Eloquent; - +use ReflectionClass; /** * Class BaseModelEloquent * @package utils\model */ abstract class BaseModelEloquent extends Eloquent { + private $class = null; /** * @param $query * @param array $filters @@ -20,4 +21,41 @@ abstract class BaseModelEloquent extends Eloquent { } return $query; } + + public function __construct($attributes = array()) + { + parent::__construct($attributes); + $this->class = new ReflectionClass(get_class($this)); + if ($this->useSti()) { + $this->setAttribute($this->stiClassField, $this->class->getName()); + } + } + + private function useSti() { + return ($this->stiClassField && $this->stiBaseClass); + } + + public function newQuery($excludeDeleted = true) + { + $builder = parent::newQuery($excludeDeleted); + // If I am using STI, and I am not the base class, + // then filter on the class name. + if ($this->useSti() && get_class(new $this->stiBaseClass) !== get_class($this)) { + $builder->where($this->stiClassField, "=", $this->class->getShortName()); + } + return $builder; + } + + public function newFromBuilder($attributes = array()) + { + if ($this->useSti() && $attributes->{$this->stiClassField}) { + $class = $this->class->getName(); + $instance = new $class; + $instance->exists = true; + $instance->setRawAttributes((array) $attributes, true); + return $instance; + } else { + return parent::newFromBuilder($attributes); + } + } } \ No newline at end of file diff --git a/app/libs/utils/services/ICacheService.php b/app/libs/utils/services/ICacheService.php index 6866a471..abbb3069 100644 --- a/app/libs/utils/services/ICacheService.php +++ b/app/libs/utils/services/ICacheService.php @@ -88,4 +88,10 @@ interface ICacheService { public function setKeyExpiration($key, $ttl); public function boot(); + + /**Returns the remaining time to live of a key that has a timeout. + * @param string $key + * @return int + */ + public function ttl($key); } \ No newline at end of file diff --git a/app/libs/utils/services/ILogService.php b/app/libs/utils/services/ILogService.php index 5fe8c746..3e041f11 100644 --- a/app/libs/utils/services/ILogService.php +++ b/app/libs/utils/services/ILogService.php @@ -9,6 +9,7 @@ use Exception; * @package utils\services */ interface ILogService { + public function error(Exception $exception); public function warning(Exception $exception); diff --git a/app/models/IBaseRepository.php b/app/models/IBaseRepository.php new file mode 100644 index 00000000..138b7f72 --- /dev/null +++ b/app/models/IBaseRepository.php @@ -0,0 +1,24 @@ +ID; + } +} \ No newline at end of file diff --git a/app/models/marketplace/Consultant.php b/app/models/marketplace/Consultant.php new file mode 100644 index 00000000..f5fd0290 --- /dev/null +++ b/app/models/marketplace/Consultant.php @@ -0,0 +1,29 @@ +hasMany('models\marketplace\Office', 'ConsultantID', 'ID')->get(); + } +} \ No newline at end of file diff --git a/app/models/marketplace/DataCenterLocation.php b/app/models/marketplace/DataCenterLocation.php new file mode 100644 index 00000000..d3a9ef7c --- /dev/null +++ b/app/models/marketplace/DataCenterLocation.php @@ -0,0 +1,36 @@ +belongsTo('models\marketplace\DataCenterRegion', 'DataCenterRegionID'); + } +} \ No newline at end of file diff --git a/app/models/marketplace/DataCenterRegion.php b/app/models/marketplace/DataCenterRegion.php new file mode 100644 index 00000000..6644c44e --- /dev/null +++ b/app/models/marketplace/DataCenterRegion.php @@ -0,0 +1,35 @@ +hasMany('models\marketplace\DataCenterLocation','DataCenterRegionID','ID')->get(); + } + +} \ No newline at end of file diff --git a/app/models/marketplace/ICloudService.php b/app/models/marketplace/ICloudService.php new file mode 100644 index 00000000..964eb581 --- /dev/null +++ b/app/models/marketplace/ICloudService.php @@ -0,0 +1,27 @@ +belongsTo('models\marketplace\Consultant', 'ConsultantID'); + } +} \ No newline at end of file diff --git a/app/models/marketplace/PrivateCloudService.php b/app/models/marketplace/PrivateCloudService.php new file mode 100644 index 00000000..e3b0ee94 --- /dev/null +++ b/app/models/marketplace/PrivateCloudService.php @@ -0,0 +1,32 @@ +hasMany('models\marketplace\DataCenterRegion', 'CloudServiceID', 'ID')->get(); + } + +} \ No newline at end of file diff --git a/app/models/marketplace/PublicCloudService.php b/app/models/marketplace/PublicCloudService.php new file mode 100644 index 00000000..08b85dfb --- /dev/null +++ b/app/models/marketplace/PublicCloudService.php @@ -0,0 +1,29 @@ +hasMany('models\marketplace\DataCenterRegion','CloudServiceID', 'ID')->get(); + } +} \ No newline at end of file diff --git a/app/models/marketplace/repositories/ICloudServiceRepository.php b/app/models/marketplace/repositories/ICloudServiceRepository.php new file mode 100644 index 00000000..a12b5570 --- /dev/null +++ b/app/models/marketplace/repositories/ICloudServiceRepository.php @@ -0,0 +1,25 @@ +attributes['active']; diff --git a/app/repositories/RepositoriesProvider.php b/app/repositories/RepositoriesProvider.php index 5a33c7bb..b69fc208 100644 --- a/app/repositories/RepositoriesProvider.php +++ b/app/repositories/RepositoriesProvider.php @@ -21,5 +21,8 @@ class RepositoriesProvider extends ServiceProvider App::singleton('openid\repositories\IOpenIdTrustedSiteRepository', 'repositories\EloquentOpenIdTrustedSiteRepository'); App::singleton('auth\IUserRepository', 'repositories\EloquentUserRepository'); App::singleton('auth\IMemberRepository', 'repositories\EloquentMemberRepository'); + App::singleton('models\marketplace\repositories\IPublicCloudServiceRepository', 'repositories\marketplace\EloquentPublicCloudServiceRepository'); + App::singleton('models\marketplace\repositories\IPrivateCloudServiceRepository', 'repositories\marketplace\EloquentPrivateCloudServiceRepository'); + App::singleton('models\marketplace\repositories\IConsultantRepository', 'repositories\marketplace\EloquentConsultantRepository'); } } \ No newline at end of file diff --git a/app/repositories/marketplace/EloquentCompanyServiceRepository.php b/app/repositories/marketplace/EloquentCompanyServiceRepository.php new file mode 100644 index 00000000..dd2dbdfe --- /dev/null +++ b/app/repositories/marketplace/EloquentCompanyServiceRepository.php @@ -0,0 +1,96 @@ +entity->find($id); + } + + /** + * @param int $page + * @param int $per_page + * @param string $status + * @param string $order_by + * @param string $order_dir + * @return \IEntity[] + */ + public function getAll($page = 1, $per_page = 1000, $status = ICompanyServiceRepository::Status_All, $order_by = ICompanyServiceRepository::Order_date, $order_dir = 'asc') + { + + $fields = array('*'); + $filters = array(); + switch($status){ + case ICompanyServiceRepository::Status_active: + array_push($filters, + array( + 'name'=>'Active', + 'op' => '=', + 'value'=> true + ) + ); + break; + case ICompanyServiceRepository::Status_non_active: + array_push($filters, + array( + 'name'=>'Active', + 'op' => '=', + 'value'=> false + ) + ); + break; + } + + DB::getPaginator()->setCurrentPage($page); + $query = $this->entity->Filter($filters); + + switch($order_by){ + case ICompanyServiceRepository::Order_date: + $query = $query->orderBy('Created', $order_dir); + break; + case ICompanyServiceRepository::Order_name: + $query = $query->orderBy('Name', $order_dir); + break; + } + + return $query->paginate($per_page, $fields)->toArray(); + } +} \ No newline at end of file diff --git a/app/repositories/marketplace/EloquentConsultantRepository.php b/app/repositories/marketplace/EloquentConsultantRepository.php new file mode 100644 index 00000000..6406acd5 --- /dev/null +++ b/app/repositories/marketplace/EloquentConsultantRepository.php @@ -0,0 +1,36 @@ +entity = $consultant; + $this->log_service = $log_service; + } +} \ No newline at end of file diff --git a/app/repositories/marketplace/EloquentPrivateCloudServiceRepository.php b/app/repositories/marketplace/EloquentPrivateCloudServiceRepository.php new file mode 100644 index 00000000..a6a124df --- /dev/null +++ b/app/repositories/marketplace/EloquentPrivateCloudServiceRepository.php @@ -0,0 +1,36 @@ +entity = $private_cloud; + $this->log_service = $log_service; + } + +} \ No newline at end of file diff --git a/app/repositories/marketplace/EloquentPublicCloudServiceRepository.php b/app/repositories/marketplace/EloquentPublicCloudServiceRepository.php new file mode 100644 index 00000000..913f4e3a --- /dev/null +++ b/app/repositories/marketplace/EloquentPublicCloudServiceRepository.php @@ -0,0 +1,41 @@ +entity = $public_cloud; + $this->log_service = $log_service; + } + +} \ No newline at end of file diff --git a/app/routes.php b/app/routes.php index b356e493..69dbea6d 100644 --- a/app/routes.php +++ b/app/routes.php @@ -180,9 +180,33 @@ Route::group(array('prefix' => 'admin/api/v1', 'before' => 'ssl|auth'), function }); //OAuth2 Protected API -Route::group(array('prefix' => 'api/v1', 'before' => 'ssl|oauth2.enabled|oauth2.cors.before|oauth2.protected.endpoint'), function() +Route::group(array('prefix' => 'api/v1', + 'before' => 'ssl|oauth2.enabled|oauth2.rate.limiter|oauth2.cors.before|oauth2.protected.endpoint', + 'after' => 'oauth2.rate.limiter.headers|oauth2.etag'), function() { Route::group(array('prefix' => 'users'), function(){ Route::get('/me','OAuth2UserApiController@me'); }); + + Route::group(array('prefix' => 'marketplace'), function(){ + + Route::group(array('prefix' => 'public-clouds'), function(){ + Route::get('','OAuth2PublicCloudApiController@getClouds'); + Route::get('/{id}','OAuth2PublicCloudApiController@getCloud'); + Route::get('/{id}/data-centers','OAuth2PublicCloudApiController@getCloudDataCenters'); + }); + + Route::group(array('prefix' => 'private-clouds'), function(){ + Route::get('','OAuth2PrivateCloudApiController@getClouds'); + Route::get('/{id}','OAuth2PrivateCloudApiController@getCloud'); + Route::get('/{id}/data-centers','OAuth2PrivateCloudApiController@getCloudDataCenters'); + }); + + Route::group(array('prefix' => 'consultants'), function(){ + Route::get('','OAuth2ConsultantsApiController@getConsultants'); + Route::get('/{id}','OAuth2ConsultantsApiController@getConsultant'); + Route::get('/{id}/offices','OAuth2ConsultantsApiController@getOffices'); + }); + + }); }); \ No newline at end of file diff --git a/app/services/oauth2/ApiEndpointService.php b/app/services/oauth2/ApiEndpointService.php index cc37da54..d5931bdb 100644 --- a/app/services/oauth2/ApiEndpointService.php +++ b/app/services/oauth2/ApiEndpointService.php @@ -74,13 +74,14 @@ class ApiEndpointService implements IApiEndpointService { * @param string $route * @param string $http_method * @param integer $api_id + * @param integer $rate_limit * @return IApiEndpoint */ - public function add($name, $description, $active,$allow_cors, $route, $http_method, $api_id) + public function add($name, $description, $active,$allow_cors, $route, $http_method, $api_id, $rate_limit) { $instance = null; - $this->tx_service->transaction(function () use ($name, $description, $active,$allow_cors, $route, $http_method, $api_id, &$instance) { + $this->tx_service->transaction(function () use ($name, $description, $active,$allow_cors, $route, $http_method, $api_id, $rate_limit, &$instance) { //check that does not exists an endpoint with same http method and same route if(ApiEndpoint::where('http_method','=',$http_method)->where('route','=',$route)->count()>0) @@ -94,7 +95,8 @@ class ApiEndpointService implements IApiEndpointService { 'route' => $route, 'http_method' => $http_method, 'api_id' => $api_id, - 'allow_cors' => $allow_cors + 'allow_cors' => $allow_cors, + 'rate_limit' => (int)$rate_limit, ) ); $instance->Save(); @@ -118,7 +120,7 @@ class ApiEndpointService implements IApiEndpointService { if(is_null($endpoint)) throw new InvalidApiEndpoint(sprintf('api endpoint id %s does not exists!',$id)); - $allowed_update_params = array('name','description','active','route','http_method','allow_cors'); + $allowed_update_params = array('name','description','active','route','http_method','allow_cors', 'rate_limit'); foreach($allowed_update_params as $param){ if(array_key_exists($param,$params)){ $endpoint->{$param} = $params[$param]; diff --git a/app/services/utils/RedisCacheService.php b/app/services/utils/RedisCacheService.php index 74a68b24..239d533e 100644 --- a/app/services/utils/RedisCacheService.php +++ b/app/services/utils/RedisCacheService.php @@ -130,4 +130,13 @@ class RedisCacheService implements ICacheService { public function setKeyExpiration($key, $ttl){ $this->redis->expire($key, intval($ttl)); } + + /**Returns the remaining time to live of a key that has a timeout. + * @param string $key + * @return int + */ + public function ttl($key) + { + return (int)$this->redis->ttl($key); + } } \ No newline at end of file diff --git a/app/tests/OAuth2ConsultantApiTest.php b/app/tests/OAuth2ConsultantApiTest.php new file mode 100644 index 00000000..3e122316 --- /dev/null +++ b/app/tests/OAuth2ConsultantApiTest.php @@ -0,0 +1,112 @@ +current_realm) + ); + return $scope; + } + + + public function testGetConsultants(){ + + $params = array( + 'page' => 1 , + 'per_page' => 10, + 'status' => ICompanyServiceRepository::Status_active, + ); + + $headers = array("HTTP_Authorization" => " Bearer " .$this->access_token); + $response = $this->action("GET", "OAuth2ConsultantsApiController@getConsultants", + $params, + array(), + array(), + $headers); + + + $content = $response->getContent(); + $consultants = json_decode($content); + + $this->assertResponseStatus(200); + } + + public function testGetConsultantNotFound(){ + + $params = array( + 'id' => 0 + ); + + $headers = array("HTTP_Authorization" => " Bearer " .$this->access_token); + $response = $this->action("GET", "OAuth2ConsultantsApiController@getConsultant", + $params, + array(), + array(), + $headers); + + + $content = $response->getContent(); + $res = json_decode($content); + + $this->assertResponseStatus(404); + } + + public function testGetConsultantFound(){ + + $params = array( + 'id' => 18 + ); + + $headers = array("HTTP_Authorization" => " Bearer " .$this->access_token); + $response = $this->action("GET", "OAuth2ConsultantsApiController@getConsultant", + $params, + array(), + array(), + $headers); + + + $content = $response->getContent(); + $res = json_decode($content); + + $this->assertResponseStatus(200); + } + + public function testGetOffices(){ + + $params = array( + 'id' => 19 + ); + + $headers = array("HTTP_Authorization" => " Bearer " .$this->access_token); + $response = $this->action("GET", "OAuth2ConsultantsApiController@getOffices", + $params, + array(), + array(), + $headers); + + + $content = $response->getContent(); + $res = json_decode($content); + + $this->assertResponseStatus(200); + + } +} \ No newline at end of file diff --git a/app/tests/OAuth2PrivateCloudApiTest.php b/app/tests/OAuth2PrivateCloudApiTest.php new file mode 100644 index 00000000..0711e8a8 --- /dev/null +++ b/app/tests/OAuth2PrivateCloudApiTest.php @@ -0,0 +1,113 @@ +current_realm) + ); + + return $scope; + } + + public function testGetPrivateClouds(){ + + $params = array( + 'page' => 1 , + 'per_page' => 10, + 'status' => ICompanyServiceRepository::Status_active, + ); + + $headers = array("HTTP_Authorization" => " Bearer " .$this->access_token); + $response = $this->action("GET", "OAuth2PrivateCloudApiController@getClouds", + $params, + array(), + array(), + $headers); + + + $content = $response->getContent(); + $clouds = json_decode($content); + + $this->assertResponseStatus(200); + } + + public function testGetPrivateCloudNotFound(){ + + $params = array( + 'id' => 0 + ); + + $headers = array("HTTP_Authorization" => " Bearer " .$this->access_token); + $response = $this->action("GET", "OAuth2PrivateCloudApiController@getCloud", + $params, + array(), + array(), + $headers); + + + $content = $response->getContent(); + $res = json_decode($content); + + $this->assertResponseStatus(404); + } + + public function testGetPrivateCloudFound(){ + + $params = array( + 'id' => 60 + ); + + $headers = array("HTTP_Authorization" => " Bearer " .$this->access_token); + $response = $this->action("GET", "OAuth2PrivateCloudApiController@getCloud", + $params, + array(), + array(), + $headers); + + + $content = $response->getContent(); + $res = json_decode($content); + + $this->assertResponseStatus(200); + } + + public function testGetDataCenterRegions(){ + + $params = array( + 'id' => 60 + ); + + $headers = array("HTTP_Authorization" => " Bearer " .$this->access_token); + $response = $this->action("GET", "OAuth2PrivateCloudApiController@getCloudDataCenters", + $params, + array(), + array(), + $headers); + + + $content = $response->getContent(); + $res = json_decode($content); + + $this->assertResponseStatus(200); + + } +} \ No newline at end of file diff --git a/app/tests/OAuth2ProtectedApiTest.php b/app/tests/OAuth2ProtectedApiTest.php new file mode 100644 index 00000000..1735ca2f --- /dev/null +++ b/app/tests/OAuth2ProtectedApiTest.php @@ -0,0 +1,100 @@ +current_realm = Config::get('app.url'); + + $user = User::where('external_id', '=', 'smarcet@gmail.com')->first(); + + $this->be($user); + + Session::start(); + + $scope = $this->getScopes(); + + $this->client_id = 'Jiz87D8/Vcvr6fvQbH4HyNgwTlfSyQ3x.openstack.client'; + $this->client_secret = 'ITc/6Y5N7kOtGKhg'; + + $params = array( + 'client_id' => $this->client_id, + 'redirect_uri' => 'https://www.test.com/oauth2', + 'response_type' => OAuth2Protocol::OAuth2Protocol_ResponseType_Code, + 'scope' => implode(' ',$scope), + OAuth2Protocol::OAuth2Protocol_AccessType =>OAuth2Protocol::OAuth2Protocol_AccessType_Offline, + ); + + + Session::set("openid.authorization.response", IAuthService::AuthorizationResponse_AllowOnce); + + $response = $this->action("POST", "OAuth2ProviderController@authorize", + $params, + array(), + array(), + array()); + + $status = $response->getStatusCode(); + $url = $response->getTargetUrl(); + $content = $response->getContent(); + + $comps = @parse_url($url); + $query = $comps['query']; + $output = array(); + parse_str($query, $output); + + $params = array( + 'code' => $output['code'], + 'redirect_uri' => 'https://www.test.com/oauth2', + 'grant_type' => OAuth2Protocol::OAuth2Protocol_GrantType_AuthCode, + ); + + $response = $this->action("POST", "OAuth2ProviderController@token", + $params, + array(), + array(), + // Symfony interally prefixes headers with "HTTP", so + array("HTTP_Authorization" => " Basic " . base64_encode($this->client_id . ':' . $this->client_secret))); + + $status = $response->getStatusCode(); + + $this->assertResponseStatus(200); + + $content = $response->getContent(); + + $response = json_decode($content); + $access_token = $response->access_token; + $refresh_token = $response->refresh_token; + + $this->access_token = $access_token; + } +} \ No newline at end of file diff --git a/app/tests/OAuth2PublicCloudApiTest.php b/app/tests/OAuth2PublicCloudApiTest.php new file mode 100644 index 00000000..bd9902a4 --- /dev/null +++ b/app/tests/OAuth2PublicCloudApiTest.php @@ -0,0 +1,113 @@ + 1 , + 'per_page' => 10, + 'status' => ICompanyServiceRepository::Status_active, + ); + + $headers = array("HTTP_Authorization" => " Bearer " .$this->access_token); + $response = $this->action("GET", "OAuth2PublicCloudApiController@getClouds", + $params, + array(), + array(), + $headers); + + + $content = $response->getContent(); + $clouds = json_decode($content); + + $this->assertResponseStatus(200); + } + + public function testGetPublicCloudNotFound(){ + + $params = array( + 'id' => 0 + ); + + $headers = array("HTTP_Authorization" => " Bearer " .$this->access_token); + $response = $this->action("GET", "OAuth2PublicCloudApiController@getCloud", + $params, + array(), + array(), + $headers); + + + $content = $response->getContent(); + $res = json_decode($content); + + $this->assertResponseStatus(404); + } + + public function testGetPublicCloudFound(){ + + $params = array( + 'id' => 17 + ); + + $headers = array("HTTP_Authorization" => " Bearer " .$this->access_token); + $response = $this->action("GET", "OAuth2PublicCloudApiController@getCloud", + $params, + array(), + array(), + $headers); + + + $content = $response->getContent(); + $res = json_decode($content); + + $this->assertResponseStatus(200); + } + + public function testGetDataCenterRegions(){ + + $params = array( + 'id' => 53 + ); + + $headers = array("HTTP_Authorization" => " Bearer " .$this->access_token); + $response = $this->action("GET", "OAuth2PublicCloudApiController@getCloudDataCenters", + $params, + array(), + array(), + $headers); + + + $content = $response->getContent(); + $res = json_decode($content); + + $this->assertResponseStatus(200); + + } + + protected function getScopes() + { + $scope = array( + sprintf('%s/public-clouds/read',$this->current_realm) + ); + + return $scope; + } +} \ No newline at end of file diff --git a/app/tests/OAuth2UserServiceApiTest.php b/app/tests/OAuth2UserServiceApiTest.php index 85be5bca..cb779d19 100644 --- a/app/tests/OAuth2UserServiceApiTest.php +++ b/app/tests/OAuth2UserServiceApiTest.php @@ -1,93 +1,12 @@ current_realm = Config::get('app.url'); - - $user = User::where('external_id', '=', 'smarcet@gmail.com')->first(); - - $this->be($user); - - Session::start(); - - $scope = array( - IUserService::UserProfileScope_Address, - IUserService::UserProfileScope_Email, - IUserService::UserProfileScope_Profile - ); - - $this->client_id = 'Jiz87D8/Vcvr6fvQbH4HyNgwTlfSyQ3x.openstack.client'; - $this->client_secret = 'ITc/6Y5N7kOtGKhg'; - - $params = array( - 'client_id' => $this->client_id, - 'redirect_uri' => 'https://www.test.com/oauth2', - 'response_type' => OAuth2Protocol::OAuth2Protocol_ResponseType_Code, - 'scope' => implode(' ',$scope), - OAuth2Protocol::OAuth2Protocol_AccessType =>OAuth2Protocol::OAuth2Protocol_AccessType_Offline, - ); - - - Session::set("openid.authorization.response", IAuthService::AuthorizationResponse_AllowOnce); - - $response = $this->action("POST", "OAuth2ProviderController@authorize", - $params, - array(), - array(), - array()); - - $status = $response->getStatusCode(); - $url = $response->getTargetUrl(); - $content = $response->getContent(); - - $comps = @parse_url($url); - $query = $comps['query']; - $output = array(); - parse_str($query, $output); - - $params = array( - 'code' => $output['code'], - 'redirect_uri' => 'https://www.test.com/oauth2', - 'grant_type' => OAuth2Protocol::OAuth2Protocol_GrantType_AuthCode, - ); - - $response = $this->action("POST", "OAuth2ProviderController@token", - $params, - array(), - array(), - // Symfony interally prefixes headers with "HTTP", so - array("HTTP_Authorization" => " Basic " . base64_encode($this->client_id . ':' . $this->client_secret))); - - $status = $response->getStatusCode(); - - $this->assertResponseStatus(200); - - $content = $response->getContent(); - - $response = json_decode($content); - $access_token = $response->access_token; - $refresh_token = $response->refresh_token; - - $this->access_token = $access_token; - } /** * @covers OAuth2UserApiController::get() @@ -119,4 +38,15 @@ class OAuth2UserServiceApiTest extends TestCase { $content = $response->getContent(); $user_info = json_decode($content); } + + protected function getScopes() + { + $scope = array( + IUserService::UserProfileScope_Address, + IUserService::UserProfileScope_Email, + IUserService::UserProfileScope_Profile + ); + + return $scope; + } } \ No newline at end of file diff --git a/app/views/oauth2/profile/admin/edit-api.blade.php b/app/views/oauth2/profile/admin/edit-api.blade.php index fb7d490a..da60293a 100644 --- a/app/views/oauth2/profile/admin/edit-api.blade.php +++ b/app/views/oauth2/profile/admin/edit-api.blade.php @@ -230,6 +230,9 @@ + + + + + +
diff --git a/public/js/oauth2/profile/admin/edit-api.js b/public/js/oauth2/profile/admin/edit-api.js index b273370d..e4490755 100644 --- a/public/js/oauth2/profile/admin/edit-api.js +++ b/public/js/oauth2/profile/admin/edit-api.js @@ -387,7 +387,8 @@ $(document).ready(function() { rules: { "name" : {required: true, nowhitespace:true,rangelength: [1, 255]}, "description":{required: true, free_text:true,rangelength: [1, 1024]}, - "route": {required: true,endpointroute:true,rangelength: [1, 1024]} + "route": {required: true,endpointroute:true,rangelength: [1, 1024]}, + "rate_limit": {required: true, number:true} } }); diff --git a/public/js/oauth2/profile/admin/edit-endpoint.js b/public/js/oauth2/profile/admin/edit-endpoint.js index e6a86c5f..b8e40ace 100644 --- a/public/js/oauth2/profile/admin/edit-endpoint.js +++ b/public/js/oauth2/profile/admin/edit-endpoint.js @@ -8,7 +8,8 @@ jQuery(document).ready(function($){ rules: { "name" : {required: true, nowhitespace:true,rangelength: [1, 255]}, "description":{required: true, free_text:true,rangelength: [1, 1024]}, - "route": {required: true, endpointroute:true,rangelength: [1, 1024]} + "route": {required: true, endpointroute:true,rangelength: [1, 1024]}, + "rate_limit": {required: true, number:true} } }); diff --git a/public/js/oauth2/profile/edit-client-tokens.js b/public/js/oauth2/profile/edit-client-tokens.js index f2192cc9..7aa16618 100644 --- a/public/js/oauth2/profile/edit-client-tokens.js +++ b/public/js/oauth2/profile/edit-client-tokens.js @@ -26,7 +26,7 @@ function updateAccessTokenList(){ 'td.lifetime':'token.lifetime', 'a@href':function(arg){ var token_value = arg.item.value; - var href = TokensUrls.AccessTokenUrls.de.ete; + var href = TokensUrls.AccessTokenUrls.delete; return href.replace('-1',token_value); }, 'a@data-value' :'token.value' diff --git a/readme.md b/readme.md index 5a547e2b..16a71635 100644 --- a/readme.md +++ b/readme.md @@ -11,6 +11,7 @@ run following commands on root folder * curl -s https://getcomposer.org/installer | php * php composer.phar install --prefer-dist + * php composer.phar dump-autoload --optimize * php artisan migrate --env=YOUR_ENVIRONMENT * php artisan db:seed --env=YOUR_ENVIRONMENT