diff --git a/app/config/local/app.php b/app/config/local/app.php index 2721b66f..a7f7d174 100644 --- a/app/config/local/app.php +++ b/app/config/local/app.php @@ -26,7 +26,7 @@ return array( | */ - 'url' => 'https://dev.openstackid.com', + 'url' => 'https://local.openstackid.openstack.org', /* |-------------------------------------------------------------------------- diff --git a/app/controllers/apis/ApiController.php b/app/controllers/apis/ApiController.php index 589e9ebd..6a9a48e9 100644 --- a/app/controllers/apis/ApiController.php +++ b/app/controllers/apis/ApiController.php @@ -107,7 +107,7 @@ class ApiController extends AbstractRESTController implements ICRUDController { try { $res = $this->api_service->delete($id); - return $res?$this->deleted():$this->error404(array('error'=>'operation failed')); + return $res ? $this->deleted() : $this->error404(array('error'=>'operation failed')); } catch (Exception $ex) { $this->log_service->error($ex); return $this->error500($ex); diff --git a/app/controllers/apis/UserApiController.php b/app/controllers/apis/UserApiController.php index ff94606a..51e5c5cb 100644 --- a/app/controllers/apis/UserApiController.php +++ b/app/controllers/apis/UserApiController.php @@ -1,8 +1,25 @@ token_service->getAccessToken($value,true); + if(is_null($token)) + throw new Exception(sprintf("access token %s expired!.",$value)); if(is_null($token->getUserId()) || intval($token->getUserId())!=intval($id)) throw new Exception(sprintf("access token %s does not belongs to user id %s!.",$value,$id)); $this->token_service->revokeAccessToken($value,true); @@ -53,6 +72,8 @@ class UserApiController extends AbstractRESTController implements ICRUDControlle break; case 'refresh_token': $token = $this->token_service->getRefreshToken($value,true); + if(is_null($token)) + throw new Exception(sprintf("access token %s expired!.",$value)); if(is_null($token->getUserId()) || intval($token->getUserId())!=intval($id)) throw new Exception(sprintf("refresh token %s does not belongs to user id %s!.",$value,$id)); $this->token_service->revokeRefreshToken($value,true); @@ -63,6 +84,10 @@ class UserApiController extends AbstractRESTController implements ICRUDControlle } return $this->ok(); } + catch(ExpiredAccessTokenException $ex1){ + $this->log_service->warning($ex1); + return $this->error404(); + } catch(Exception $ex){ $this->log_service->error($ex); return $this->error500($ex); diff --git a/app/controllers/apis/protected/OAuth2UserApiController.php b/app/controllers/apis/protected/OAuth2UserApiController.php index 382ae0a3..f48894e5 100644 --- a/app/controllers/apis/protected/OAuth2UserApiController.php +++ b/app/controllers/apis/protected/OAuth2UserApiController.php @@ -29,5 +29,4 @@ class OAuth2UserApiController extends OAuth2ProtectedController { return $this->error500($ex); } } -} - +} \ No newline at end of file diff --git a/app/controllers/apis/protected/marketplace/OAuth2CloudApiController.php b/app/controllers/apis/protected/marketplace/OAuth2CloudApiController.php deleted file mode 100644 index 29ef0169..00000000 --- a/app/controllers/apis/protected/marketplace/OAuth2CloudApiController.php +++ /dev/null @@ -1,81 +0,0 @@ -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 deleted file mode 100644 index 46813d96..00000000 --- a/app/controllers/apis/protected/marketplace/OAuth2CompanyServiceApiController.php +++ /dev/null @@ -1,133 +0,0 @@ - '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 deleted file mode 100644 index a5162e8d..00000000 --- a/app/controllers/apis/protected/marketplace/OAuth2ConsultantsApiController.php +++ /dev/null @@ -1,82 +0,0 @@ -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 deleted file mode 100644 index 727bed68..00000000 --- a/app/controllers/apis/protected/marketplace/OAuth2PrivateCloudApiController.php +++ /dev/null @@ -1,27 +0,0 @@ -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 deleted file mode 100644 index 56465062..00000000 --- a/app/controllers/apis/protected/marketplace/OAuth2PublicCloudApiController.php +++ /dev/null @@ -1,29 +0,0 @@ -repository = $repository; - } - -} \ No newline at end of file diff --git a/app/database/migrations/2013_10_14_155702_create_extension_table.php b/app/database/migrations/2013_10_14_155702_create_extension_table.php index 20f1030f..a0ff9acd 100644 --- a/app/database/migrations/2013_10_14_155702_create_extension_table.php +++ b/app/database/migrations/2013_10_14_155702_create_extension_table.php @@ -14,12 +14,12 @@ class CreateExtensionTable extends Migration { Schema::create('server_extensions', function($table) { $table->bigIncrements('id'); - $table->string('name',100); - $table->string('namespace',255); + $table->string('name',100)->nullable()->default(''); + $table->string('namespace',255)->nullable()->default(''); $table->boolean('active')->default(false); - $table->string('extension_class',255); - $table->string('description',255)->nullable(); - $table->string('view_name',255); + $table->string('extension_class',255)->nullable()->default(''); + $table->string('description',255)->nullable()->default(''); + $table->string('view_name',255)->nullable()->default(''); $table->timestamps(); }); } diff --git a/app/database/migrations/2013_10_17_210518_create_users_table.php b/app/database/migrations/2013_10_17_210518_create_users_table.php index 5dfd8756..096e8bce 100644 --- a/app/database/migrations/2013_10_17_210518_create_users_table.php +++ b/app/database/migrations/2013_10_17_210518_create_users_table.php @@ -14,14 +14,14 @@ class CreateUsersTable extends Migration { Schema::create('openid_users', function($table) { $table->bigIncrements('id'); - $table->string('identifier',255); + $table->string('identifier',255)->nullable(); $table->string('external_id',100); $table->boolean('active')->default(true); $table->boolean('lock')->default(false); $table->boolean('public_profile_show_photo')->default(false); $table->boolean('public_profile_show_fullname')->default(false); $table->boolean('public_profile_show_email')->default(false); - $table->dateTime('last_login_date'); + $table->dateTime('last_login_date')->default(date("Y-m-d H:i:s")); $table->integer('login_failed_attempt')->default(0); $table->timestamps(); }); diff --git a/app/database/migrations/2013_12_04_181145_create_oauth2_clients_table.php b/app/database/migrations/2013_12_04_181145_create_oauth2_clients_table.php index 9a6bd2cb..ec49ebd5 100644 --- a/app/database/migrations/2013_12_04_181145_create_oauth2_clients_table.php +++ b/app/database/migrations/2013_12_04_181145_create_oauth2_clients_table.php @@ -14,8 +14,8 @@ class CreateOauth2ClientsTable extends Migration { Schema::create('oauth2_client', function($table) { $table->bigIncrements('id')->unsigned(); - $table->string('app_name',255); - $table->text('app_description'); + $table->string('app_name',255)->nullable(); + $table->text('app_description')->nullable(); $table->string('app_logo',255)->nullable(); $table->string('client_id',255)->unique(); $table->string('client_secret',255)->nullable(); @@ -30,13 +30,13 @@ class CreateOauth2ClientsTable extends Migration { $table->timestamps(); $table->integer('max_auth_codes_issuance_qty')->default(0); - $table->smallInteger('max_auth_codes_issuance_basis'); + $table->smallInteger('max_auth_codes_issuance_basis')->default(0); $table->integer('max_access_token_issuance_qty')->default(0); - $table->smallInteger('max_access_token_issuance_basis'); + $table->smallInteger('max_access_token_issuance_basis')->default(0); $table->integer('max_refresh_token_issuance_qty')->default(0); - $table->smallInteger('max_refresh_token_issuance_basis'); + $table->smallInteger('max_refresh_token_issuance_basis')->default(0); $table->boolean('use_refresh_token')->default(false); $table->boolean('rotate_refresh_token')->default(false); diff --git a/app/database/migrations/2015_04_28_194733_update_oauth2_api_table.php b/app/database/migrations/2015_04_28_194733_update_oauth2_api_table.php new file mode 100644 index 00000000..4f9f2787 --- /dev/null +++ b/app/database/migrations/2015_04_28_194733_update_oauth2_api_table.php @@ -0,0 +1,35 @@ +dropUnique('oauth2_api_name_unique'); + $table->unique(array('name','resource_server_id')); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('oauth2_api', function($table) + { + + }); + } + +} diff --git a/app/database/migrations/2015_04_28_195254_update_oauth2_api_endpoint_table.php b/app/database/migrations/2015_04_28_195254_update_oauth2_api_endpoint_table.php new file mode 100644 index 00000000..1e877d0f --- /dev/null +++ b/app/database/migrations/2015_04_28_195254_update_oauth2_api_endpoint_table.php @@ -0,0 +1,33 @@ +dropUnique('oauth2_api_endpoint_name_unique'); + $table->unique(array('name','http_method','api_id')); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } + +} diff --git a/app/database/migrations/2015_05_06_142958_delete_marketplace_api_endpoints_scopes.php b/app/database/migrations/2015_05_06_142958_delete_marketplace_api_endpoints_scopes.php new file mode 100644 index 00000000..871e827c --- /dev/null +++ b/app/database/migrations/2015_05_06_142958_delete_marketplace_api_endpoints_scopes.php @@ -0,0 +1,33 @@ +first(); + if($public_clouds) $public_clouds->delete(); + $private_clouds = Api::where('name','=','private-clouds')->first(); + if($private_clouds) $private_clouds->delete(); + $consultants = Api::where('name','=','consultants')->first(); + if($consultants) $consultants->delete(); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } + +} diff --git a/app/database/seeds/ApiEndpointSeeder.php b/app/database/seeds/ApiEndpointSeeder.php index ead48802..03ef0c52 100644 --- a/app/database/seeds/ApiEndpointSeeder.php +++ b/app/database/seeds/ApiEndpointSeeder.php @@ -12,9 +12,6 @@ class ApiEndpointSeeder extends Seeder DB::table('oauth2_api_endpoint_api_scope')->delete(); DB::table('oauth2_api_endpoint')->delete(); $this->seedUsersEndpoints(); - $this->seedPublicCloudsEndpoints(); - $this->seedPrivateCloudsEndpoints(); - $this->seedConsultantsEndpoints(); } private function seedUsersEndpoints() @@ -42,146 +39,4 @@ 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 0997669c..7e7ac270 100644 --- a/app/database/seeds/ApiScopeSeeder.php +++ b/app/database/seeds/ApiScopeSeeder.php @@ -5,16 +5,12 @@ */ class ApiScopeSeeder extends Seeder { - public function run() { DB::table('oauth2_api_endpoint_api_scope')->delete(); DB::table('oauth2_client_api_scope')->delete(); DB::table('oauth2_api_scope')->delete(); $this->seedUsersScopes(); - $this->seedPublicCloudScopes(); - $this->seedPrivateCloudScopes(); - $this->seedConsultantScopes(); } private function seedUsersScopes(){ @@ -53,53 +49,4 @@ 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 7994c1f2..afea2c1c 100644 --- a/app/database/seeds/ApiSeeder.php +++ b/app/database/seeds/ApiSeeder.php @@ -25,38 +25,5 @@ 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/OpenIdExtensionsSeeder.php b/app/database/seeds/OpenIdExtensionsSeeder.php index d06f4302..67761d37 100644 --- a/app/database/seeds/OpenIdExtensionsSeeder.php +++ b/app/database/seeds/OpenIdExtensionsSeeder.php @@ -23,7 +23,7 @@ class OpenIdExtensionsSeeder extends Seeder { 'namespace' => 'http://openid.net/extensions/sreg/1.1', 'active' => true, 'extension_class' => 'openid\extensions\implementations\OpenIdSREGExtension', - 'description' => 'OpenID Simple Registation is an extension to the OpenID Authentication protocol that allows for very light-weight profile exchange. It is designed to pass eight commonly requested pieces of information when an End User goes to register a new account with a web service', + 'description' => 'OpenID Simple Registration is an extension to the OpenID Authentication protocol that allows for very light-weight profile exchange.', 'view_name' => 'extensions.sreg', ) ); @@ -35,7 +35,7 @@ class OpenIdExtensionsSeeder extends Seeder { 'namespace' => 'http://specs.openid.net/extensions/oauth/2.0', 'active' => true, 'extension_class' => 'openid\extensions\implementations\OpenIdOAuth2Extension', - 'description' => 'The OpenID OAuth2 Extension describes how to make the OpenID Authentication and OAuth2 Core specifications work well togethe', + 'description' => 'The OpenID OAuth2 Extension describes how to make the OpenID Authentication and OAuth2 Core specifications work well together.', 'view_name' => 'extensions.oauth2', ) ); diff --git a/app/database/seeds/TestSeeder.php b/app/database/seeds/TestSeeder.php index 00280094..5d4cc395 100644 --- a/app/database/seeds/TestSeeder.php +++ b/app/database/seeds/TestSeeder.php @@ -38,11 +38,13 @@ class TestSeeder extends Seeder { $this->seedServerConfiguration(); $this->seedServerExtensions(); + $current_realm = Config::get('app.url'); + $components = parse_url($current_realm); ResourceServer::create( array( 'friendly_name' => 'test resource server', - 'host' => 'dev.openstackid.com', + 'host' => $components['host'], 'ip' => '127.0.0.1' ) ); @@ -281,7 +283,7 @@ class TestSeeder extends Seeder { 'namespace' => 'http://openid.net/extensions/sreg/1.1', 'active' => true, 'extension_class' => 'openid\extensions\implementations\OpenIdSREGExtension', - 'description' => 'OpenID Simple Registation is an extension to the OpenID Authentication protocol that allows for very light-weight profile exchange. It is designed to pass eight commonly requested pieces of information when an End User goes to register a new account with a web service', + 'description' => 'OpenID Simple Registration is an extension to the OpenID Authentication protocol that allows for very light-weight profile exchange.', 'view_name' => 'extensions.sreg', ) ); @@ -292,7 +294,7 @@ class TestSeeder extends Seeder { 'namespace' => 'http://specs.openid.net/extensions/oauth/2.0', 'active' => true, 'extension_class' => 'openid\extensions\implementations\OpenIdOAuth2Extension', - 'description' => 'The OpenID OAuth2 Extension describes how to make the OpenID Authentication and OAuth2 Core specifications work well togethe', + 'description' => 'The OpenID OAuth2 Extension describes how to make the OpenID Authentication and OAuth2 Core specifications work well together.', 'view_name' => 'extensions.oauth2', ) ); diff --git a/app/filters.php b/app/filters.php index 1950eb3c..ce811342 100644 --- a/app/filters.php +++ b/app/filters.php @@ -36,8 +36,22 @@ App::before(function($request){ }); App::after(function($request, $response){ + // https://www.owasp.org/index.php/List_of_useful_HTTP_headers $response->headers->set('X-content-type-options','nosniff'); $response->headers->set('X-xss-protection','1; mode=block'); + // http://tools.ietf.org/html/rfc6797 + /** + * The HSTS header field below stipulates that the HSTS Policy is to + * remain in effect for one year (there are approximately 31536000 + * seconds in a year) + * applies to the domain of the issuing HSTS Host and all of its + * subdomains: + */ + $response->headers->set('Strict-Transport-Security','max-age=31536000; includeSubDomains'); + //cache + $response->headers->set('pragma','no-cache'); + $response->headers->set('Expires','-1'); + $response->headers->set('cache-control','no-store, must-revalidate, no-cache'); $cors = ServiceLocator::getInstance()->getService('CORSMiddleware'); $cors->modifyResponse($request, $response); }); @@ -207,12 +221,6 @@ 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 deleted file mode 100644 index de9f4d04..00000000 --- a/app/filters/ApiEndpointRateLimiter.php +++ /dev/null @@ -1,84 +0,0 @@ -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 deleted file mode 100644 index bf44756b..00000000 --- a/app/filters/ApiEndpointRateLimiterHeaders.php +++ /dev/null @@ -1,87 +0,0 @@ -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 deleted file mode 100644 index 951c9ec3..00000000 --- a/app/filters/ETagChecker.php +++ /dev/null @@ -1,36 +0,0 @@ -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/filters/OAuth2RequestAccessTokenValidator.php b/app/filters/OAuth2RequestAccessTokenValidator.php index 921717e7..2e1511ff 100644 --- a/app/filters/OAuth2RequestAccessTokenValidator.php +++ b/app/filters/OAuth2RequestAccessTokenValidator.php @@ -8,10 +8,12 @@ use oauth2\responses\OAuth2WWWAuthenticateErrorResponse; use utils\services\ILogService; use oauth2\exceptions\OAuth2ResourceServerException; use oauth2\exceptions\InvalidGrantTypeException; +use oauth2\exceptions\ExpiredAccessTokenException; use utils\services\ICheckPointService; use oauth2\IResourceServerContext; use oauth2\services\IClientService; use oauth2\models\IClient; + /** * Class OAuth2BearerAccessTokenRequestValidator * this class implements the logic to Accessing to Protected Resources @@ -107,7 +109,8 @@ class OAuth2BearerAccessTokenRequestValidator { // get access token from service $access_token = $this->token_service->getAccessToken($access_token_value); - + if(is_null($access_token)) + throw new ExpiredAccessTokenException(sprintf('Access token %s is expired!', $access_token_value)); //check token audience $audience = explode(' ',$access_token->getAudience()); if((!in_array($realm,$audience))) @@ -177,6 +180,19 @@ class OAuth2BearerAccessTokenRequestValidator { $http_response->header('WWW-Authenticate',$response->getWWWAuthenticateHeaderValue()); return $http_response; } + catch(ExpiredAccessTokenException $ex3){ + $this->log_service->error($ex3); + $this->checkpoint_service->trackException($ex3); + $response = new OAuth2WWWAuthenticateErrorResponse($realm, + OAuth2Protocol::OAuth2Protocol_Error_InvalidToken, + 'the access token provided is expired, revoked, malformed, or invalid for other reasons.', + null, + 401 + ); + $http_response = Response::json($response->getContent(), $response->getHttpCode()); + $http_response->header('WWW-Authenticate',$response->getWWWAuthenticateHeaderValue()); + return $http_response; + } catch(Exception $ex){ $this->log_service->error($ex); $this->checkpoint_service->trackException($ex); diff --git a/app/libs/oauth2/IResourceServerContext.php b/app/libs/oauth2/IResourceServerContext.php index 59c9a3d2..17097e5f 100644 --- a/app/libs/oauth2/IResourceServerContext.php +++ b/app/libs/oauth2/IResourceServerContext.php @@ -10,13 +10,13 @@ namespace oauth2; interface IResourceServerContext { /** - * returns given scopes for current requewt + * returns given scopes for current request * @return array */ public function getCurrentScope(); /** - * gets current access token valaue + * gets current access token values * @return string */ public function getCurrentAccessToken(); diff --git a/app/libs/oauth2/OAuth2Protocol.php b/app/libs/oauth2/OAuth2Protocol.php index b1be337a..3f60a365 100644 --- a/app/libs/oauth2/OAuth2Protocol.php +++ b/app/libs/oauth2/OAuth2Protocol.php @@ -31,6 +31,7 @@ use oauth2\exceptions\UriNotAllowedException; use oauth2\exceptions\MissingClientAuthorizationInfo; use oauth2\exceptions\InvalidRedeemAuthCodeException; use oauth2\exceptions\InvalidClientCredentials; +use oauth2\exceptions\ExpiredAccessTokenException; //grant types use oauth2\grant_types\AuthorizationCodeGrantType; @@ -120,7 +121,7 @@ class OAuth2Protocol implements IOAuth2Protocol const OAuth2Protocol_Error_ServerError = 'server_error'; const OAuth2Protocol_Error_TemporallyUnavailable = 'temporally_unavailable'; //http://tools.ietf.org/html/rfc7009#section-2.2.1 - const OAuth2Protocol_Error_Unsupported_TokenType = ' unsupported_token_type'; + const OAuth2Protocol_Error_Unsupported_TokenType = 'unsupported_token_type'; //http://tools.ietf.org/html/rfc6750#section-3-1 const OAuth2Protocol_Error_InvalidToken = 'invalid_token'; const OAuth2Protocol_Error_InsufficientScope = 'insufficient_scope'; @@ -488,6 +489,11 @@ class OAuth2Protocol implements IOAuth2Protocol $this->checkpoint_service->trackException($ex3); return new OAuth2DirectErrorResponse(OAuth2Protocol::OAuth2Protocol_Error_UnauthorizedClient); } + catch(ExpiredAccessTokenException $ex4){ + $this->log_service->warning($ex4); + $this->checkpoint_service->trackException($ex4); + return new OAuth2DirectErrorResponse(OAuth2Protocol::OAuth2Protocol_Error_InvalidToken); + } catch (Exception $ex) { $this->log_service->error($ex); $this->checkpoint_service->trackException($ex); diff --git a/app/models/marketplace/Consultant.php b/app/libs/oauth2/exceptions/ExpiredAccessTokenException.php similarity index 61% rename from app/models/marketplace/Consultant.php rename to app/libs/oauth2/exceptions/ExpiredAccessTokenException.php index f5fd0290..672043c5 100644 --- a/app/models/marketplace/Consultant.php +++ b/app/libs/oauth2/exceptions/ExpiredAccessTokenException.php @@ -1,6 +1,6 @@ hasMany('models\marketplace\Office', 'ConsultantID', 'ID')->get(); + public function __construct($message = "") + { + $message = "Expired Access Token: " . $message; + parent::__construct($message, 0, null); } + } \ No newline at end of file diff --git a/app/libs/oauth2/exceptions/InvalidAllowedClientUriException.php b/app/libs/oauth2/exceptions/InvalidAllowedClientUriException.php new file mode 100644 index 00000000..1bf394bd --- /dev/null +++ b/app/libs/oauth2/exceptions/InvalidAllowedClientUriException.php @@ -0,0 +1,16 @@ +token_service->getAccessToken($token_value); + + if(is_null($access_token)) + throw new ExpiredAccessTokenException(sprintf('Access token %s is expired!', $token_value)); + if ($access_token->getClientId() !== $this->current_client_id) throw new BearerTokenDisclosureAttemptException($this->current_client_id,sprintf('access token %s does not belongs to client id %s',$token_value, $this->current_client_id)); @@ -106,6 +111,7 @@ class RevokeBearerTokenGrantType extends AbstractGrantType { //check ownership $refresh_token = $this->token_service->getRefreshToken($token_value); + if ($refresh_token->getClientId() !== $this->current_client_id) throw new BearerTokenDisclosureAttemptException($this->current_client_id,sprintf('refresh token %s does not belongs to client id %s',$token_value, $this->current_client_id)); @@ -125,8 +131,13 @@ class RevokeBearerTokenGrantType extends AbstractGrantType try{ //check ownership $access_token = $this->token_service->getAccessToken($token_value); + + if(is_null($access_token)) + throw new ExpiredAccessTokenException(sprintf('Access token %s is expired!', $token_value)); + if ($access_token->getClientId() !== $this->current_client_id) throw new BearerTokenDisclosureAttemptException($this->current_client_id,sprintf('access token %s does not belongs to client id %s',$token_value, $this->current_client_id)); + $this->token_service->revokeAccessToken($token_value, false); } catch(UnAuthorizedClientException $ex1){ diff --git a/app/libs/oauth2/grant_types/ValidateBearerTokenGrantType.php b/app/libs/oauth2/grant_types/ValidateBearerTokenGrantType.php index 170a75fd..f0c57874 100644 --- a/app/libs/oauth2/grant_types/ValidateBearerTokenGrantType.php +++ b/app/libs/oauth2/grant_types/ValidateBearerTokenGrantType.php @@ -2,6 +2,7 @@ namespace oauth2\grant_types; +use oauth2\exceptions\ExpiredAccessTokenException; use oauth2\exceptions\InvalidApplicationType; use oauth2\exceptions\InvalidOAuth2Request; use oauth2\exceptions\InvalidAccessTokenException; @@ -102,7 +103,8 @@ class ValidateBearerTokenGrantType extends AbstractGrantType try{ $access_token = $this->token_service->getAccessToken($token_value); - + if(is_null($access_token)) + throw new ExpiredAccessTokenException(sprintf('Access token %s is expired!', $token_value)); if(!$this->current_client->isResourceServerClient()){ // if current client is not a resource server, then we could only access to our own tokens if($access_token->getClientId()!== $this->current_client_id) @@ -127,16 +129,21 @@ class ValidateBearerTokenGrantType extends AbstractGrantType } $allowed_origins = array(); - foreach($this->current_client->getClientAllowedOrigins() as $origin){ + $allowed_urls = array(); + $issued_client = $this->client_service->getClientById($access_token->getClientId()); + + if(is_null($issued_client)) + throw new BearerTokenDisclosureAttemptException($this->current_client_id,sprintf('access token %s does not belongs to client id %s',$token_value, $access_token->getClientId())); + + foreach($issued_client->getClientAllowedOrigins() as $origin){ array_push($allowed_origins, $origin->allowed_origin); } - $allowed_urls = array(); - foreach($this->current_client->getClientRegisteredUris() as $url){ + foreach($issued_client->getClientRegisteredUris() as $url){ array_push($allowed_urls, $url->uri); } - return new OAuth2AccessTokenValidationResponse($token_value, $access_token->getScope(), $access_token->getAudience(), $access_token->getClientId(), $access_token->getRemainingLifetime(), $access_token->getUserId(), $allowed_urls, $allowed_origins); + return new OAuth2AccessTokenValidationResponse($token_value, $access_token->getScope(), $access_token->getAudience(), $access_token->getClientId(), $access_token->getRemainingLifetime(), $access_token->getUserId(), $issued_client->getApplicationType(), $allowed_urls, $allowed_origins); } catch(InvalidAccessTokenException $ex1){ $this->log_service->error($ex1); diff --git a/app/libs/oauth2/responses/OAuth2AccessTokenValidationResponse.php b/app/libs/oauth2/responses/OAuth2AccessTokenValidationResponse.php index c2475383..1a542ff2 100644 --- a/app/libs/oauth2/responses/OAuth2AccessTokenValidationResponse.php +++ b/app/libs/oauth2/responses/OAuth2AccessTokenValidationResponse.php @@ -6,7 +6,18 @@ use oauth2\OAuth2Protocol; class OAuth2AccessTokenValidationResponse extends OAuth2DirectResponse { - public function __construct($access_token,$scope, $audience,$client_id,$expires_in, $user_id = null, $allowed_urls = array(), $allowed_origins = array()) + /** + * @param array|int $access_token + * @param string $scope + * @param $audience + * @param $client_id + * @param $expires_in + * @param null $user_id + * @param null $application_type + * @param array $allowed_urls + * @param array $allowed_origins + */ + public function __construct($access_token,$scope, $audience, $client_id, $expires_in, $user_id = null, $application_type = null, $allowed_urls = array(), $allowed_origins = array()) { // Successful Responses: A server receiving a valid request MUST send a // response with an HTTP status code of 200. @@ -22,6 +33,10 @@ class OAuth2AccessTokenValidationResponse extends OAuth2DirectResponse { $this[OAuth2Protocol::OAuth2Protocol_UserId] = $user_id; } + if(!is_null($application_type)){ + $this['application_type'] = $application_type; + } + if(count($allowed_urls)){ $this['allowed_return_uris'] = implode(' ', $allowed_urls); } diff --git a/app/models/marketplace/CompanyService.php b/app/models/marketplace/CompanyService.php deleted file mode 100644 index 91ad7fe4..00000000 --- a/app/models/marketplace/CompanyService.php +++ /dev/null @@ -1,40 +0,0 @@ -ID; - } -} \ No newline at end of file diff --git a/app/models/marketplace/DataCenterLocation.php b/app/models/marketplace/DataCenterLocation.php deleted file mode 100644 index d3a9ef7c..00000000 --- a/app/models/marketplace/DataCenterLocation.php +++ /dev/null @@ -1,36 +0,0 @@ -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 deleted file mode 100644 index 6644c44e..00000000 --- a/app/models/marketplace/DataCenterRegion.php +++ /dev/null @@ -1,35 +0,0 @@ -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 deleted file mode 100644 index 964eb581..00000000 --- a/app/models/marketplace/ICloudService.php +++ /dev/null @@ -1,27 +0,0 @@ -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 deleted file mode 100644 index e3b0ee94..00000000 --- a/app/models/marketplace/PrivateCloudService.php +++ /dev/null @@ -1,32 +0,0 @@ -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 deleted file mode 100644 index 08b85dfb..00000000 --- a/app/models/marketplace/PublicCloudService.php +++ /dev/null @@ -1,29 +0,0 @@ -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 deleted file mode 100644 index a12b5570..00000000 --- a/app/models/marketplace/repositories/ICloudServiceRepository.php +++ /dev/null @@ -1,25 +0,0 @@ -active = $active; } + + public function delete () + { + $endpoints = ApiEndpoint::where('api_id','=', $this->id)->get(); + foreach($endpoints as $endpoint){ + $endpoint->delete(); + } + + $scopes = ApiScope::where('api_id','=', $this->id)->get(); + foreach($scopes as $scope){ + $scope->delete(); + } + + return parent::delete(); + } } \ No newline at end of file diff --git a/app/models/oauth2/Client.php b/app/models/oauth2/Client.php index 42c4b4a4..864af077 100644 --- a/app/models/oauth2/Client.php +++ b/app/models/oauth2/Client.php @@ -121,16 +121,15 @@ class Client extends BaseModelEloquent implements IClient { } if(($parts['scheme']!=='https') && (ServerConfigurationService::getConfigValue("SSL.Enable"))) return false; - $client_authorized_uri = ClientAuthorizedUri::where('client_id', '=', $this->id)->where('uri','=',$uri)->first(); - if(!is_null($client_authorized_uri)) return true; - - if(isset($parts['path'])){ - $aux_uri = $parts['scheme'].'://'.strtolower($parts['host']).strtolower($parts['path']); - $client_authorized_uri = ClientAuthorizedUri::where('client_id', '=', $this->id)->where('uri','=',$aux_uri)->first(); - return !is_null($client_authorized_uri); + //normalize uri + $normalized_uri = $parts['scheme'].'://'.strtolower($parts['host']); + if(isset($parts['path'])) { + $normalized_uri .= strtolower($parts['path']); } - return false; - + // normalize url and remove trailing / + $normalized_uri = rtrim($normalized_uri, '/'); + $client_authorized_uri = ClientAuthorizedUri::where('client_id', '=', $this->id)->where('uri','=',$normalized_uri)->first(); + return !is_null($client_authorized_uri); } public function getApplicationName() diff --git a/app/repositories/EloquentUserRepository.php b/app/repositories/EloquentUserRepository.php index 2bcc4457..8e87e01f 100644 --- a/app/repositories/EloquentUserRepository.php +++ b/app/repositories/EloquentUserRepository.php @@ -89,6 +89,6 @@ class EloquentUserRepository implements IUserRepository { */ public function getByToken($identifier, $token) { - return $this->user->where('id', '=', $identifier)->where('remember_token', '=', $token)->first(); + return $this->user->where('external_identifier', '=', $identifier)->where('remember_token', '=', $token)->first(); } } \ No newline at end of file diff --git a/app/repositories/RepositoriesProvider.php b/app/repositories/RepositoriesProvider.php index b69fc208..5a33c7bb 100644 --- a/app/repositories/RepositoriesProvider.php +++ b/app/repositories/RepositoriesProvider.php @@ -21,8 +21,5 @@ 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 deleted file mode 100644 index dd2dbdfe..00000000 --- a/app/repositories/marketplace/EloquentCompanyServiceRepository.php +++ /dev/null @@ -1,96 +0,0 @@ -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 deleted file mode 100644 index 6406acd5..00000000 --- a/app/repositories/marketplace/EloquentConsultantRepository.php +++ /dev/null @@ -1,36 +0,0 @@ -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 deleted file mode 100644 index a6a124df..00000000 --- a/app/repositories/marketplace/EloquentPrivateCloudServiceRepository.php +++ /dev/null @@ -1,36 +0,0 @@ -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 deleted file mode 100644 index 913f4e3a..00000000 --- a/app/repositories/marketplace/EloquentPublicCloudServiceRepository.php +++ /dev/null @@ -1,41 +0,0 @@ -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 69dbea6d..9c7f6b19 100644 --- a/app/routes.php +++ b/app/routes.php @@ -181,32 +181,10 @@ 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.rate.limiter|oauth2.cors.before|oauth2.protected.endpoint', - 'after' => 'oauth2.rate.limiter.headers|oauth2.etag'), function() + 'before' => 'ssl|oauth2.enabled|oauth2.protected.endpoint', + 'after' => ''), 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 d5931bdb..af3d34d3 100644 --- a/app/services/oauth2/ApiEndpointService.php +++ b/app/services/oauth2/ApiEndpointService.php @@ -84,7 +84,7 @@ class ApiEndpointService implements IApiEndpointService { $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) + if(ApiEndpoint::where('http_method','=',$http_method)->where('route','=',$route)->where('api_id','=',$api_id)->count()>0) throw new InvalidApiEndpoint(sprintf('there is already an endpoint api with route %s and http method %s',$route,$http_method)); $instance = new ApiEndpoint( @@ -127,7 +127,7 @@ class ApiEndpointService implements IApiEndpointService { } } //check that does not exists an endpoint with same http method and same route - if(ApiEndpoint::where('http_method','=',$endpoint->http_method)->where('route','=',$endpoint->route)->where('id','<>',$endpoint->id)->count()>0) + if(ApiEndpoint::where('http_method', '=' , $endpoint->http_method)->where('route', '=', $endpoint->route)->where('id', '<>' ,$endpoint->id)->where('api_id','=',$endpoint->api_id)->count()>0) throw new InvalidApiEndpoint(sprintf('there is already an endpoint api with route %s and http method %s',$endpoint->route,$endpoint->http_method)); $res = $this_var->save($endpoint); }); diff --git a/app/services/oauth2/ApiService.php b/app/services/oauth2/ApiService.php index abc9b384..1277c109 100644 --- a/app/services/oauth2/ApiService.php +++ b/app/services/oauth2/ApiService.php @@ -73,7 +73,7 @@ class ApiService implements IApiService { $this->tx_service->transaction(function () use ($name, $description, $active, $resource_server_id, &$instance) { - $count = Api::where('name','=',$name)->count(); + $count = Api::where('name','=',$name)->where('resource_server_id','=',$resource_server_id)->count(); if($count>0) throw new InvalidApi(sprintf('api name %s already exists!',$name)); @@ -112,7 +112,7 @@ class ApiService implements IApiService { if(array_key_exists($param,$params)){ if($param=='name'){ - if(Api::where('name','=',$params[$param])->where('id','<>',$id)->count()>0) + if(Api::where('name','=',$params[$param])->where('id','<>',$id)->where('resource_server_id','=',$api->resource_server_id)->count()>0) throw new InvalidApi(sprintf('api name %s already exists!',$params[$param])); } diff --git a/app/services/oauth2/CORS/CORSMiddleware.php b/app/services/oauth2/CORS/CORSMiddleware.php index f67f4439..d7b04376 100644 --- a/app/services/oauth2/CORS/CORSMiddleware.php +++ b/app/services/oauth2/CORS/CORSMiddleware.php @@ -13,7 +13,7 @@ use App; use Log; use Exception; use Config; - +use Illuminate\Support\Facades\Cache; /** * Class CORSMiddleware * @package services\oauth2 @@ -25,7 +25,7 @@ class CORSMiddleware { private $cache_service; private $origin_service; private $actual_request = false; - private $headers = array(); + private $headers = array(); private $allowed_headers; private $allowed_methods; /** diff --git a/app/services/oauth2/ClientService.php b/app/services/oauth2/ClientService.php index 87f9a225..e0b1f39b 100644 --- a/app/services/oauth2/ClientService.php +++ b/app/services/oauth2/ClientService.php @@ -12,6 +12,7 @@ use oauth2\exceptions\AllowedClientUriAlreadyExistsException; use oauth2\exceptions\InvalidClientType; use oauth2\exceptions\MissingClientAuthorizationInfo; use oauth2\exceptions\AbsentClientException; +use oauth2\exceptions\InvalidAllowedClientUriException; use oauth2\models\IClient; use oauth2\OAuth2Protocol; @@ -135,9 +136,24 @@ class ClientService implements IClientService if (is_null($client)) throw new AbsentClientException(sprintf("client id %s does not exists!",$id)); - $client_uri = ClientAuthorizedUri::where('uri', '=', $uri)->where('client_id', '=', $id)->first(); + if(!filter_var($uri, FILTER_VALIDATE_URL)) return false; + $parts = @parse_url($uri); + if (!$parts) { + throw new InvalidAllowedClientUriException(sprintf('uri : %s', $uri)); + } + if(($parts['scheme']!=='https') && (ServerConfigurationService::getConfigValue("SSL.Enable"))) + throw new InvalidAllowedClientUriException(sprintf('uri : %s', $uri)); + //normalize uri + $normalized_uri = $parts['scheme'].'://'.strtolower($parts['host']); + if(isset($parts['path'])) { + $normalized_uri .= strtolower($parts['path']); + } + // normalize url and remove trailing / + $normalized_uri = rtrim($normalized_uri, '/'); + + $client_uri = ClientAuthorizedUri::where('uri', '=', $normalized_uri)->where('client_id', '=', $id)->first(); if (!is_null($client_uri)) { - throw new AllowedClientUriAlreadyExistsException(sprintf('uri : %s', $uri)); + throw new AllowedClientUriAlreadyExistsException(sprintf('uri : %s', $normalized_uri)); } $client_authorized_uri = new ClientAuthorizedUri; diff --git a/app/services/oauth2/TokenService.php b/app/services/oauth2/TokenService.php index 6deaddcc..357b4791 100644 --- a/app/services/oauth2/TokenService.php +++ b/app/services/oauth2/TokenService.php @@ -1,4 +1,16 @@ $code->getAccessType(), 'approval_prompt' => $code->getApprovalPrompt(), 'has_previous_user_consent' => $code->getHasPreviousUserConsent() - ), $code->getLifetime()); + ), intval($code->getLifetime())); //stores brand new auth code hash value on a set by client id... $this->cache_service->addMemberSet($client_id . self::ClientAuthCodePrefixList, $hashed_value); @@ -397,9 +411,7 @@ class TokenService implements ITokenService 'lifetime' => $access_token->getLifetime(), 'audience' => $access_token->getAudience(), 'from_ip' => IPHelper::getUserIp(), - 'refresh_token' => $refresh_token_value, - $access_token->getLifetime() - )); + 'refresh_token' => $refresh_token_value), intval($access_token->getLifetime())); } /** @@ -437,79 +449,102 @@ class TokenService implements ITokenService /** * @param $value - * @param $is_hashed + * @param bool $is_hashed * @return AccessToken - * @throws \oauth2\exceptions\InvalidAccessTokenException - * @throws \oauth2\exceptions\InvalidGrantTypeException + * @throws InvalidAccessTokenException + * @throws \Exception */ public function getAccessToken($value, $is_hashed = false) { + $cache_service = $this->cache_service; + $lock_manager_service = $this->lock_manager_service; + $configuration_service = $this->configuration_service; + $this_var = $this; - //hash the given value, bc tokens values are stored hashed on DB - $hashed_value = !$is_hashed ? Hash::compute('sha256', $value):$value; - - try { - if (!$this->cache_service->exists($hashed_value)) { - //check on DB... - $access_token_db = DBAccessToken::where('value', '=', $hashed_value)->first(); - if (is_null($access_token_db)) - throw new InvalidGrantTypeException(sprintf("Access token %s is invalid!", $value)); - //lock ... - $lock_name = 'lock.get.accesstoken.' . $hashed_value; - $this->lock_manager_service->acquireLock($lock_name); - - - if ($access_token_db->isVoid()){ - //invalid one ... - $access_token_db->delete(); - throw new InvalidGrantTypeException(sprintf('Access token %s is expired!', $value)); + return $this->tx_service->transaction(function () use ($this_var, $value, $is_hashed, $cache_service, $lock_manager_service, $configuration_service){ + //hash the given value, bc tokens values are stored hashed on DB + $hashed_value = !$is_hashed ? Hash::compute('sha256', $value):$value; + $lock_name = ''; + $access_token = null; + try { + // check cache ... + if (!$cache_service->exists($hashed_value)) { + // check on DB... + $access_token_db = DBAccessToken::where('value', '=', $hashed_value)->first(); + if (is_null($access_token_db)) { + if($cache_service->exists('access.token:void:'.$hashed_value)) // check if its marked on cache as expired ... + throw new ExpiredAccessTokenException(sprintf('Access token %s is expired!', $value)); + else + throw new InvalidGrantTypeException(sprintf("Access token %s is invalid!", $value)); + } + // lock ... + $lock_name = 'lock.get.accesstoken.' . $hashed_value; + $lock_manager_service->acquireLock($lock_name); + if ($access_token_db->isVoid()){ + // invalid one ... + // add to cache as expired ... + $cache_service->addSingleValue('access.token:void:'.$hashed_value, 'access.token:void:'.$hashed_value); + // and deleted it from db + $access_token_db->delete(); + throw new ExpiredAccessTokenException(sprintf('Access token %s is expired!', $value)); + } + //reload on cache + $this_var->storesDBAccessTokenOnCache($access_token_db); + //release lock + $lock_manager_service->releaseLock($lock_name); + } + + $cache_values = $cache_service->getHash($hashed_value, array( + 'user_id', + 'client_id', + 'scope', + 'auth_code', + 'issued', + 'lifetime', + 'from_ip', + 'audience', + 'refresh_token' + )); + + // reload auth code ... + $auth_code = AuthorizationCode::load( + $cache_values['auth_code'], + intval($cache_values['user_id']) == 0 ? null : intval($cache_values['user_id']), + $cache_values['client_id'], + $cache_values['scope'], + $cache_values['audience'], + null, + null, + $configuration_service->getConfigValue('OAuth2.AuthorizationCode.Lifetime'), + $cache_values['from_ip'], + $access_type = OAuth2Protocol::OAuth2Protocol_AccessType_Online, + $approval_prompt = OAuth2Protocol::OAuth2Protocol_Approval_Prompt_Auto, + $has_previous_user_consent = false, + $is_hashed = true + ); + // reload access token ... + $access_token = AccessToken::load($value, $auth_code, $cache_values['issued'],$cache_values['lifetime']); + $refresh_token_value = $cache_values['refresh_token']; + + if(!empty($refresh_token_value)){ + $refresh_token = $this_var->getRefreshToken($refresh_token_value,true); + $access_token->setRefreshToken($refresh_token); } - //reload on cache - $this->storesDBAccessTokenOnCache($access_token_db); - //release lock - $this->lock_manager_service->releaseLock($lock_name); } - - $cache_values = $this->cache_service->getHash($hashed_value, array( - 'user_id', - 'client_id', - 'scope', - 'auth_code', - 'issued', - 'lifetime', - 'from_ip', - 'audience', - 'refresh_token' - )); - - // reload auth code ... - $auth_code = AuthorizationCode::load( - $cache_values['auth_code'], - intval($cache_values['user_id'])==0?null:intval($cache_values['user_id']), - $cache_values['client_id'], - $cache_values['scope'], - $cache_values['audience'], - null, - null, - $this->configuration_service->getConfigValue('OAuth2.AuthorizationCode.Lifetime'), - $cache_values['from_ip'], - $access_type = OAuth2Protocol::OAuth2Protocol_AccessType_Online, - $approval_prompt = OAuth2Protocol::OAuth2Protocol_Approval_Prompt_Auto, - $has_previous_user_consent=false, - $is_hashed = true - ); - // reload access token ... - $access_token = AccessToken::load($value, $auth_code, $cache_values['issued'],$cache_values['lifetime']); - $refresh_token_value = $cache_values['refresh_token']; - - if(!empty($refresh_token_value)){ - $refresh_token = $this->getRefreshToken($refresh_token_value,true); - $access_token->setRefreshToken($refresh_token); + catch (UnacquiredLockException $ex1) { + throw new InvalidAccessTokenException("access token %s ", $value); } - } catch (UnacquiredLockException $ex1) { - throw new InvalidAccessTokenException("access token %s ", $value); - } - return $access_token; + catch(ExpiredAccessTokenException $ex2){ + if(!empty($lock_name)) + $lock_manager_service->releaseLock($lock_name); + } + catch(\Exception $ex){ + if(!empty($lock_name)) + $lock_manager_service->releaseLock($lock_name); + throw $ex; + } + return $access_token; + }); } /** diff --git a/app/services/openid/UserService.php b/app/services/openid/UserService.php index a6c3159a..8b9b17a8 100644 --- a/app/services/openid/UserService.php +++ b/app/services/openid/UserService.php @@ -41,7 +41,7 @@ class UserService implements IUserService { try { $repository = $this->repository; - if (!is_null($user) && $user->identifier === strval($user->external_identifier)) { + if (!is_null($user) && strval($user->identifier) === strval($user->external_identifier)) { $this->tx_service->transaction(function () use ($proposed_username,&$user,&$repository) { $done = false; diff --git a/app/services/utils/EloquentTransactionService.php b/app/services/utils/EloquentTransactionService.php index 3936845d..72458fd0 100644 --- a/app/services/utils/EloquentTransactionService.php +++ b/app/services/utils/EloquentTransactionService.php @@ -22,6 +22,6 @@ class EloquentTransactionService implements ITransactionService { */ public function transaction(Closure $callback) { - DB::transaction($callback); + return DB::transaction($callback); } } \ No newline at end of file diff --git a/app/services/utils/RedisCacheService.php b/app/services/utils/RedisCacheService.php index 239d533e..e9059d77 100644 --- a/app/services/utils/RedisCacheService.php +++ b/app/services/utils/RedisCacheService.php @@ -19,7 +19,6 @@ class RedisCacheService implements ICacheService { } - public function boot(){ if(is_null($this->redis)){ $this->redis = \RedisLV4::connection(); @@ -48,7 +47,7 @@ class RedisCacheService implements ICacheService { * @return bool */ public function exists($key){ - $res = $this->redis->exists($key); + $res = $this->redis->exists($key); return $res>0; } @@ -82,10 +81,12 @@ class RedisCacheService implements ICacheService { public function incCounter($counter_name, $ttl = 0) { - if($this->redis->setnx($counter_name,1)) - $this->redis->expire($counter_name, $ttl); + if($this->redis->setnx($counter_name,1)) { + $this->redis->expire($counter_name, $ttl); + return 1; + } else - $this->redis->incr($counter_name); + return (int)$this->redis->incr($counter_name); } public function incCounterIfExists($counter_name){ @@ -122,7 +123,7 @@ class RedisCacheService implements ICacheService { public function addSingleValue($key, $value, $ttl = 0){ $res = $this->redis->setnx($key , $value); - if($res>0 && $ttl>0) + if($res && $ttl>0) $this->redis->expire($key,$ttl); return $res; } diff --git a/app/start/global.php b/app/start/global.php index 5f369b69..43e677ed 100644 --- a/app/start/global.php +++ b/app/start/global.php @@ -16,6 +16,7 @@ use utils\services\UtilsServiceCatalog; use oauth2\exceptions\InvalidOAuth2Request; use Monolog\Logger; use Monolog\Handler\NativeMailerHandler; +use Illuminate\Support\Facades\App; ClassLoader::addDirectories(array( @@ -91,31 +92,37 @@ if (Config::get('database.log', false)){ App::error(function (Exception $exception, $code) { - $checkpoint_service = ServiceLocator::getInstance()->getService(UtilsServiceCatalog::CheckPointService); Log::error($exception); - if($checkpoint_service ){ - $checkpoint_service->trackException($exception); + if(!App::runningInConsole()) { + $checkpoint_service = ServiceLocator::getInstance()->getService(UtilsServiceCatalog::CheckPointService); + if ($checkpoint_service) { + $checkpoint_service->trackException($exception); + } + return View::make('404'); } - return View::make('404'); }); App::error(function (InvalidOpenIdMessageException $exception, $code) { - $checkpoint_service = ServiceLocator::getInstance()->getService(UtilsServiceCatalog::CheckPointService); Log::error($exception); - if($checkpoint_service ){ - $checkpoint_service->trackException($exception); + if(!App::runningInConsole()) { + $checkpoint_service = ServiceLocator::getInstance()->getService(UtilsServiceCatalog::CheckPointService); + if ($checkpoint_service) { + $checkpoint_service->trackException($exception); + } + return View::make('404'); } - return View::make('404'); }); App::error(function (InvalidOAuth2Request $exception, $code) { - $checkpoint_service = ServiceLocator::getInstance()->getService(UtilsServiceCatalog::CheckPointService); Log::error($exception); - if($checkpoint_service ){ - $checkpoint_service->trackException($exception); + if(!App::runningInConsole()) { + $checkpoint_service = ServiceLocator::getInstance()->getService(UtilsServiceCatalog::CheckPointService); + if ($checkpoint_service) { + $checkpoint_service->trackException($exception); + } + return View::make('404'); } - return View::make('404'); }); diff --git a/app/tests/ApiTest.php b/app/tests/ApiTest.php index bf30a4b2..0f8f0447 100644 --- a/app/tests/ApiTest.php +++ b/app/tests/ApiTest.php @@ -10,11 +10,15 @@ class ApiTest extends TestCase { private $current_realm; + private $current_host; + protected function prepareForTests() { parent::prepareForTests(); //Route::enableFilters(); $this->current_realm = Config::get('app.url'); + $parts = parse_url($this->current_realm); + $this->current_host = $parts['host']; } public function testGetById(){ @@ -50,7 +54,7 @@ class ApiTest extends TestCase { public function testCreate(){ - $resource_server = ResourceServer::where('host','=','dev.openstackid.com')->first(); + $resource_server = ResourceServer::where('host','=', $this->current_host)->first(); $data = array( 'name' => 'test-api', @@ -74,7 +78,7 @@ class ApiTest extends TestCase { public function testDelete(){ - $resource_server = ResourceServer::where('host','=','dev.openstackid.com')->first(); + $resource_server = ResourceServer::where('host','=', $this->current_host)->first(); $data = array( 'name' => 'test-api', @@ -116,7 +120,7 @@ class ApiTest extends TestCase { public function testUpdate(){ - $resource_server = ResourceServer::where('host','=','dev.openstackid.com')->first(); + $resource_server = ResourceServer::where('host','=',$this->current_host)->first(); $data = array( 'name' => 'test-api', @@ -173,7 +177,7 @@ class ApiTest extends TestCase { public function testUpdateStatus(){ - $resource_server = ResourceServer::where('host','=','dev.openstackid.com')->first(); + $resource_server = ResourceServer::where('host','=',$this->current_host)->first(); $data = array( 'name' => 'test-api', diff --git a/app/tests/OAuth2ConsultantApiTest.php b/app/tests/OAuth2ConsultantApiTest.php deleted file mode 100644 index 3e122316..00000000 --- a/app/tests/OAuth2ConsultantApiTest.php +++ /dev/null @@ -1,112 +0,0 @@ -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 deleted file mode 100644 index 0711e8a8..00000000 --- a/app/tests/OAuth2PrivateCloudApiTest.php +++ /dev/null @@ -1,113 +0,0 @@ -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 index 822b1f96..ed046561 100644 --- a/app/tests/OAuth2ProtectedApiTest.php +++ b/app/tests/OAuth2ProtectedApiTest.php @@ -1,6 +1,6 @@ current_realm = Config::get('app.url'); $user = User::where('identifier','=','sebastian.marcet')->first(); @@ -211,6 +224,112 @@ class OAuth2ProtocolTest extends OpenStackIDBaseTest } } + + /** test validate token grant + * @throws Exception + */ + public function testValidateExpiredToken() + { + + try { + // set token lifetime + $_ENV['access.token.lifetime'] = 1; + + $client_id = 'Jiz87D8/Vcvr6fvQbH4HyNgwTlfSyQ3x.openstack.client'; + $client_secret = 'ITc/6Y5N7kOtGKhg'; + + Session::set("openid.authorization.response", IAuthService::AuthorizationResponse_AllowOnce); + + //do authorization ... + + $params = array( + 'client_id' => $client_id, + 'redirect_uri' => 'https://www.test.com/oauth2', + 'response_type' => OAuth2Protocol::OAuth2Protocol_ResponseType_Code, + OAuth2Protocol::OAuth2Protocol_AccessType =>OAuth2Protocol::OAuth2Protocol_AccessType_Offline, + 'scope' => sprintf('%s/resource-server/read', $this->current_realm), + ); + + $response = $this->action("POST", "OAuth2ProviderController@authorize", + $params, + array(), + array(), + array()); + + $status = $response->getStatusCode(); + $url = $response->getTargetUrl(); + $content = $response->getContent(); + + // get auth code ... + $comps = @parse_url($url); + $query = $comps['query']; + $output = array(); + parse_str($query, $output); + + + //do get auth token... + $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($client_id . ':' . $client_secret))); + + $this->assertResponseStatus(200); + + $content = $response->getContent(); + + $response = json_decode($content); + //get access token and refresh token... + $access_token = $response->access_token; + $refresh_token = $response->refresh_token; + + $this->assertTrue(!empty($access_token)); + $this->assertTrue(!empty($refresh_token)); + sleep(2); + //do token validation .... + $params = array( + 'token' => $access_token, + ); + + $response = $this->action("POST", "OAuth2ProviderController@introspection", + $params, + array(), + array(), + // Symfony interally prefixes headers with "HTTP", so + array("HTTP_Authorization" => " Basic " . base64_encode($client_id . ':' . $client_secret))); + + $this->assertResponseStatus(400); + + $content = $response->getContent(); + + $response = json_decode($content); + + $response = $this->action("POST", "OAuth2ProviderController@introspection", + $params, + array(), + array(), + // Symfony interally prefixes headers with "HTTP", so + array("HTTP_Authorization" => " Basic " . base64_encode($client_id . ':' . $client_secret))); + + $this->assertResponseStatus(400); + + $content = $response->getContent(); + + $response = json_decode($content); + + } catch (Exception $ex) { + throw $ex; + } + } + /** test refresh token grant * @throws Exception */ diff --git a/app/tests/OAuth2PublicCloudApiTest.php b/app/tests/OAuth2PublicCloudApiTest.php deleted file mode 100644 index bd9902a4..00000000 --- a/app/tests/OAuth2PublicCloudApiTest.php +++ /dev/null @@ -1,113 +0,0 @@ - 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 cb779d19..1c4b6ddd 100644 --- a/app/tests/OAuth2UserServiceApiTest.php +++ b/app/tests/OAuth2UserServiceApiTest.php @@ -24,7 +24,7 @@ class OAuth2UserServiceApiTest extends OAuth2ProtectedApiTest { } public function testGetInfoCORS(){ - $response = $this->action("OPTION", "OAuth2UserApiController@me", + $response = $this->action("OPTIONS", "OAuth2UserApiController@me", array(), array(), array(), diff --git a/app/tests/OpenStackIDBaseTest.php b/app/tests/OpenStackIDBaseTest.php index 7d12cf26..5ac37ec5 100644 --- a/app/tests/OpenStackIDBaseTest.php +++ b/app/tests/OpenStackIDBaseTest.php @@ -19,26 +19,40 @@ abstract class OpenStackIDBaseTest extends TestCase { protected function prepareForTests() { - DB::table('banned_ips')->delete(); - DB::table('user_exceptions_trail')->delete(); - DB::table('server_configuration')->delete(); - DB::table('server_extensions')->delete(); - - DB::table('oauth2_client_api_scope')->delete(); - DB::table('oauth2_client_authorized_uri')->delete(); - DB::table('oauth2_access_token')->delete(); - DB::table('oauth2_refresh_token')->delete(); - DB::table('oauth2_client')->delete(); - - DB::table('openid_trusted_sites')->delete(); - DB::table('openid_associations')->delete(); - DB::table('openid_users')->delete(); - - DB::table('oauth2_api_endpoint_api_scope')->delete(); - DB::table('oauth2_api_endpoint')->delete(); - DB::table('oauth2_api_scope')->delete(); - DB::table('oauth2_api')->delete(); - DB::table('oauth2_resource_server')->delete(); + if (Schema::hasTable('banned_ips')) + DB::table('banned_ips')->delete(); + if (Schema::hasTable('user_exceptions_trail')) + DB::table('user_exceptions_trail')->delete(); + if (Schema::hasTable('server_configuration')) + DB::table('server_configuration')->delete(); + if (Schema::hasTable('server_extensions')) + DB::table('server_extensions')->delete(); + if (Schema::hasTable('oauth2_client_api_scope')) + DB::table('oauth2_client_api_scope')->delete(); + if (Schema::hasTable('oauth2_client_authorized_uri')) + DB::table('oauth2_client_authorized_uri')->delete(); + if (Schema::hasTable('oauth2_access_token')) + DB::table('oauth2_access_token')->delete(); + if (Schema::hasTable('oauth2_refresh_token')) + DB::table('oauth2_refresh_token')->delete(); + if (Schema::hasTable('oauth2_client')) + DB::table('oauth2_client')->delete(); + if (Schema::hasTable('openid_trusted_sites')) + DB::table('openid_trusted_sites')->delete(); + if (Schema::hasTable('openid_associations')) + DB::table('openid_associations')->delete(); + if (Schema::hasTable('openid_users')) + DB::table('openid_users')->delete(); + if (Schema::hasTable('oauth2_api_endpoint_api_scope')) + DB::table('oauth2_api_endpoint_api_scope')->delete(); + if (Schema::hasTable('oauth2_api_endpoint')) + DB::table('oauth2_api_endpoint')->delete(); + if (Schema::hasTable('oauth2_api_scope')) + DB::table('oauth2_api_scope')->delete(); + if (Schema::hasTable('oauth2_api')) + DB::table('oauth2_api')->delete(); + if (Schema::hasTable('oauth2_resource_server')) + DB::table('oauth2_resource_server')->delete(); parent::prepareForTests(); } diff --git a/app/tests/ResourceServerApiTest.php b/app/tests/ResourceServerApiTest.php index 34398205..a153e355 100644 --- a/app/tests/ResourceServerApiTest.php +++ b/app/tests/ResourceServerApiTest.php @@ -9,16 +9,20 @@ class ResourceServerApiTest extends TestCase { private $current_realm; + private $current_host; + protected function prepareForTests() { parent::prepareForTests(); //Route::enableFilters(); $this->current_realm = Config::get('app.url'); + $parts = parse_url($this->current_realm); + $this->current_host = $parts['host']; } public function testGetById(){ - $resource_server = ResourceServer::where('host','=','dev.openstackid.com')->first(); + $resource_server = ResourceServer::where('host','=', $this->current_host)->first(); $response = $this->action("GET", "ApiResourceServerController@get", $parameters = array('id' => $resource_server->id), @@ -169,7 +173,7 @@ class ResourceServerApiTest extends TestCase { public function testDeleteExistingOne(){ - $resource_server = ResourceServer::where('host','=','dev.openstackid.com')->first(); + $resource_server = ResourceServer::where('host','=', $this->current_host)->first(); $new_id = $resource_server->id; diff --git a/app/views/oauth2/profile/admin/edit-api.blade.php b/app/views/oauth2/profile/admin/edit-api.blade.php index da60293a..c9e68ce4 100644 --- a/app/views/oauth2/profile/admin/edit-api.blade.php +++ b/app/views/oauth2/profile/admin/edit-api.blade.php @@ -283,6 +283,10 @@ deactivate: '{{ URL::action("ApiEndpointController@deactivate",array("id"=>"@id")) }}', add : '{{ URL::action("ApiEndpointController@create") }}' }; + + var editApiMessages = { + success: '{{ Lang::get("messages.global_successfully_save_entity", array("entity" => "API")) }}' + }; {{ HTML::script('js/oauth2/profile/admin/edit-api.js') }} @stop \ No newline at end of file diff --git a/doc/source/index.rst b/doc/source/index.rst index 85d8e36e..658db9d6 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -57,12 +57,4 @@ Developer docs :maxdepth: 2 openid - oauth2 - -Client API Reference --------------------- - -.. toctree:: - :maxdepth: 1 - - restapi/v1 \ No newline at end of file + oauth2 \ No newline at end of file diff --git a/doc/source/oauth2.rst b/doc/source/oauth2.rst index 79f36d99..1a94a6e7 100644 --- a/doc/source/oauth2.rst +++ b/doc/source/oauth2.rst @@ -456,7 +456,15 @@ Below is a table of the fields included in the non-error case: | userid | This field is only present if a resource owner (end-user) had approved access on the consent screen. | | | | +------------------------------+---------------------------------------------------------------------------------------------------------------------------------+ - +| application_type | identifies the client type. (WEB_APPLICATION, JS_CLIENT OR SERVICE ) | +| | | ++------------------------------+---------------------------------------------------------------------------------------------------------------------------------+ +| allowed_return_uris | identifies the allowed return uris set for this client. | +| | | ++------------------------------+---------------------------------------------------------------------------------------------------------------------------------+ +| allowed_origins | This field is only present if application_type == JS_CLIENT. | +| | identifies the allowed origin uris set for this client. | ++------------------------------+---------------------------------------------------------------------------------------------------------------------------------+ A response from such a request is shown below:: @@ -467,9 +475,11 @@ A response from such a request is shown below:: "expires_in":3600, "token_type":"Bearer", "scope":"profile email", - "audience": "resource.server1.com" - "user_id": 123456 - + "audience": "resource.server1.com", + "user_id": 123456, + "application_type": "WEB_APPLICATION", + "allowed_return_uris": "www.test.com", + "allowed_origins": "www.test1.com", } Using OAuth 2.0 for Client-side Applications @@ -526,6 +536,40 @@ Be sure that OpenStackID Endpoint API that your application wants to access it's been `CORS `_ enabled +User API +-------- + +Allows to get additional info about current user (Me) + +.. http:get:: api/v1/users/me + + Gets additional information about the current user + + **Example request**: + + .. sourcecode:: http + + GET /api/v1/users/me HTTP/1.1 + Host: openstackid.org + Accept: application/json, text/javascript + + **Example response**: + + .. sourcecode:: http + + { + + "name":"Sebastian", + "family_name":"Marcet", + "nickname":"Sebastian Marcet", + "picture":"http:\/\/www.openstack.org\/assets\/profile-images\/IMG-20140912-WA0003.jpg", + "birthdate":"", + "gender":"Male", + "email":"sebastian@tipit.net" + + } + + Using OAuth 2.0 for Server to Server Applications ------------------------------------------------- diff --git a/doc/source/restapi/v1.rst b/doc/source/restapi/v1.rst deleted file mode 100644 index bd771250..00000000 --- a/doc/source/restapi/v1.rst +++ /dev/null @@ -1,594 +0,0 @@ -================== -OAuth 2.0 Rest API -================== - -Schema -^^^^^^ - -All API access is over HTTPS, and accessed from the **https://openstackid.org/** -domain. All data is sent and received as JSON. - -Parameters -^^^^^^^^^^ - -Many API methods take optional parameters. For GET requests, any parameters not -specified as a segment in the path can be passed as an HTTP query string - -Pagination -^^^^^^^^^^ - -Requests that return multiple items will be paginated to 10 items by default. -You can specify further pages with the **?page** parameter. For some -resources, you can also set a custom page size up to 100 with the **?per_page** -parameter. - -Rate Limiting -^^^^^^^^^^^^^ - -This is configured per API endpoint. -You can check the returned HTTP headers of any API request to see your current -rate limit status:: - - X-RateLimit-Limit: 60 - X-RateLimit-Remaining: 56 - X-RateLimit-Reset: 1372700873 - - -The headers tell you everything you need to know about your current rate limit -status : - -======================= ============================================================================== -Header Name Description -======================= ============================================================================== -X-RateLimit-Limit The maximum number of requests that the consumer is permitted to make per hour. -X-RateLimit-Remaining The number of requests remaining in the current rate limit window. -X-RateLimit-Reset The number of seconds remaining until the current rate limit window resets. -======================= ============================================================================== - -If your application triggers this rate limit, you'll receive an informative -response: - - **Example response**: - - .. sourcecode:: http - - HTTP/1.1 403 Forbidden - Content-Type: application/json; charset=utf-8 - Connection: close - - { - - "message": "You have triggered an abuse detection mechanism and have been - temporarily blocked from content creation. Please retry your request again - later." - - } - -Conditional requests -^^^^^^^^^^^^^^^^^^^^ - -Most responses return an **ETag** header. You can use the values -of this headers to make subsequent requests to those resources using the -**If-None-Match** header, respectively. If the resource -has not changed, the server will return a **304 Not Modified**. - - -Cross Origin Resource Sharing -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The API supports Cross Origin Resource Sharing (CORS) for AJAX requests from -any origin. -You can read the [CORS W3C Recommendation](http://www.w3.org/TR/cors), or -[this intro] -(http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity) from -the HTML 5 Security Guide. - -JSON-P Callbacks -^^^^^^^^^^^^^^^^ - -You can send a **?callback** parameter to any GET call to have the results -wrapped in a JSON function. This is typically used when browsers want to -embed OpenStack content in web pages by getting around cross domain issues. -The response includes the same data output as the regular API, plus the -relevant HTTP Header information. - - -User API -^^^^^^^^ - -Allows to get additional info about current user (Me) - -.. http:get:: api/v1/users/me - - Gets additional information about the current user - - **Example request**: - - .. sourcecode:: http - - GET /api/v1/users/me HTTP/1.1 - Host: openstackid.org - Accept: application/json, text/javascript - - **Example response**: - - .. sourcecode:: http - - { - - "name":"Sebastian", - "family_name":"Marcet", - "nickname":"Sebastian Marcet", - "picture":"http:\/\/www.openstack.org\/assets\/profile-images\/IMG-20140912-WA0003.jpg", - "birthdate":"", - "gender":"Male", - "email":"sebastian@tipit.net" - - } - -MarketPlace API -^^^^^^^^^^^^^^^ - -Public Clouds Endpoints ------------------------ - -Allows to get read only access to public clouds related data ( clouds and data -centers locations) - -.. http:get:: /api/v1/marketplace/public-clouds - - Get a list of public clouds - - **Example request**: - - .. sourcecode:: http - - GET /api/v1/marketplace/public-clouds HTTP/1.1 - Host: openstackid.org - Accept: application/json, text/javascript - - **Example response**: - - .. sourcecode:: http - - HTTP/1.1 200 OK - Content-Type: text/javascript - - { - - "total":20, - "per_page":10, - "current_page":1, - "last_page":2, - "from":1, - "to":10, - "data":[ - { - "ID":"YYYY", - "Created":"2014-04-23 05:36:10", - "LastEdited":"2015-02-04 11:13:58", - "Name":"Next-Generation AgileCLOUD", - "Slug":"next-generation-agilecloud", - "Overview":"....", - "Call2ActionUri":"http:\/\/....", - "Active":"1", - "CompanyID":"XXX" - } - ,{...} - ] - - } - - :query page: used in conjunction with "per_page" query string parameter. - indicates the desired page number, when we want paginate - over results - :query per_page: used in conjunction with "page" query string parameter. - indicates the desired page size - :query status: (optional filter) allow us to get active, non active or all - public clouds - :query order_by: (optional) used in conjunction with query string parameter - "order_dir", point out the desired order of the result (date or name) - :query order_dir: (optional) used in conjunction with query string parameter - "order", point out the desired order direction of the result (asc or desc) - :reqheader Authorization: OAuth 2.0 Bearer Access Token - - :statuscode 200: no error - :statuscode 412: invalid parameters - :statuscode 500: server error - -.. http:get:: api/v1/marketplace/public-clouds/(int:id) - - Get desired public cloud point out by `id` param - - **Example request**: - - .. sourcecode:: http - - GET /api/v1/marketplace/public-clouds/123456 HTTP/1.1 - Host: openstackid.org - Accept: application/json, text/javascript - - **Example response**: - - .. sourcecode:: http - - HTTP/1.1 200 OK - Vary: Accept - Content-Type: text/javascript - - { - "ID":"123456", - "Created":"2014-04-23 05:36:10", - "LastEdited":"2015-02-04 11:13:58", - "Name":"test public cloud", - "Slug":"test-public-cloud", - "Overview":"lorep ip sum", - "Call2ActionUri":"http:\/\/.../", - "Active":"1", - "CompanyID":"123456" - } - - :reqheader Authorization: OAuth 2.0 Bearer Access Token - - :statuscode 200: no error - :statuscode 404: entity not found - :statuscode 500: server error - - -.. http:get:: /api/v1/marketplace/public-clouds/(int:id)/data-centers - - Get data center locations for public cloud pointed out by `id` param - - **Example request**: - - .. sourcecode:: http - - GET /api/v1/marketplace/public-clouds/123456/data-centers HTTP/1.1 - Host: openstackid.org - Accept: application/json, text/javascript - - **Example response**: - - .. sourcecode:: http - - HTTP/1.1 200 OK - Vary: Accept - Content-Type: text/javascript - - {"datacenters":[ - { - "ID":"72", - "Created":"2014-05-07 15:19:39", - "LastEdited":"2014-05-07 15:19:39", - "Name":"West", - "Endpoint":"https:\/\/identity.uswest1.cloud.io.com\/v2.0", - "Color":"000000", - "locations":[ - { - "ID":"109", - "Created":"2014-05-07 15:19:39", - "LastEdited":"2014-05-07 15:19:39", - "City":"Phoenix", - "State":"AZ", - "Country":"US", - "Lat":"33.45", - "Lng":"-112.07" - } - ] - },... - ] - } - - :reqheader Authorization: OAuth 2.0 Bearer Access Token - - :statuscode 200: no error - :statuscode 404: entity not found (cloud) - :statuscode 500: server error - -Private Clouds Endpoints ------------------------- - -Allows to get read only access to private clouds related data ( clouds and data -centers locations) - -.. http:get:: /api/v1/marketplace/private-clouds - - Get a list of private clouds - - **Example request**: - - .. sourcecode:: http - - GET /api/v1/marketplace/private-clouds HTTP/1.1 - Host: openstackid.org - Accept: application/json, text/javascript - - **Example response**: - - .. sourcecode:: http - - HTTP/1.1 200 OK - Content-Type: text/javascript - - { - - "total":20, - "per_page":10, - "current_page":1, - "last_page":2, - "from":1, - "to":10, - "data":[ - { - "ID":"YYYY", - "Created":"2014-04-23 05:36:10", - "LastEdited":"2015-02-04 11:13:58", - "Name":"test private cloud", - "Slug":"test-private-cloud", - "Overview":"....", - "Call2ActionUri":"http:\/\/....", - "Active":"1", - "CompanyID":"XXX" - } - ,{...} - ] - - } - - :query page: used in conjunction with "per_page" query string parameter. - indicates the desired page number, when we want paginate - over results - :query per_page: used in conjunction with "page" query string parameter. - indicates the desired page size - :query status: (optional filter) allow us to get active, non active or all - public clouds - :query order_by: (optional) used in conjunction with query string parameter - "order_dir", point out the desired order of the result (date or name) - :query order_dir: (optional) used in conjunction with query string parameter - "order", point out the desired order direction of the result (asc or desc) - - :reqheader Authorization: OAuth 2.0 Bearer Access Token - - :statuscode 200: no error - :statuscode 412: invalid parameters - :statuscode 500: server error - -.. http:get:: /api/v1/marketplace/private-clouds/(int:id) - - Get desired private cloud point out by `id` param - - **Example request**: - - .. sourcecode:: http - - GET /api/v1/marketplace/private-clouds/123456 HTTP/1.1 - Host: openstackid.org - Accept: application/json, text/javascript - - **Example response**: - - .. sourcecode:: http - - HTTP/1.1 200 OK - Vary: Accept - Content-Type: text/javascript - - { - "ID":"123456", - "Created":"2014-04-23 05:36:10", - "LastEdited":"2015-02-04 11:13:58", - "Name":"test private cloud", - "Slug":"test-private-cloud", - "Overview":"lorep ip sum", - "Call2ActionUri":"http:\/\/..", - "Active":"1", - "CompanyID":"123456" - } - - :reqheader Authorization: OAuth 2.0 Bearer Access Token - - :statuscode 200: no error - :statuscode 404: entity not found - :statuscode 500: server error - - -.. http:get:: /api/v1/marketplace/private-clouds/(int:id)/data-centers - - Get data center locations for private cloud pointed out by `id` param - - **Example request**: - - .. sourcecode:: http - - GET /api/v1/marketplace/private-clouds/123456/data-centers HTTP/1.1 - Host: openstackid.org - Accept: application/json, text/javascript - - **Example response**: - - .. sourcecode:: http - - HTTP/1.1 200 OK - Vary: Accept - Content-Type: text/javascript - - {"datacenters":[ - { - "ID":"72", - "Created":"2014-05-07 15:19:39", - "LastEdited":"2014-05-07 15:19:39", - "Name":"West", - "Endpoint":"https:\/\/identity.uswest1.cloud.io.com\/v2.0", - "Color":"000000", - "locations":[ - { - "ID":"109", - "Created":"2014-05-07 15:19:39", - "LastEdited":"2014-05-07 15:19:39", - "City":"Phoenix", - "State":"AZ", - "Country":"US", - "Lat":"33.45", - "Lng":"-112.07" - } - ] - },... - ] - } - - :reqheader Authorization: OAuth 2.0 Bearer Access Token - - :statuscode 200: no error - :statuscode 404: entity not found (cloud) - :statuscode 500: server error - - -Consultants Endpoints ---------------------- - -Allows to get read only access to consultants related data ( consultants and -offices locations) - -.. http:get:: /api/v1/marketplace/consultants - - Get a list of consultants - - **Example request**: - - .. sourcecode:: http - - GET /api/v1/marketplace/consultants HTTP/1.1 - Host: openstackid.org - Accept: application/json, text/javascript - - **Example response**: - - .. sourcecode:: http - - HTTP/1.1 200 OK - Content-Type: text/javascript - - { - - "total":20, - "per_page":10, - "current_page":1, - "last_page":2, - "from":1, - "to":10, - "data":[ - { - "ID":"YYYY", - "Created":"2014-04-23 05:36:10", - "LastEdited":"2015-02-04 11:13:58", - "Name":"Consultant Name", - "Slug":"consultant-name", - "Overview":"....", - "Call2ActionUri":"http:\/\/....", - "Active":"1", - "CompanyID":"XXX" - } - ,{...} - ] - - } - - :query page: used in conjunction with "per_page" query string parameter. - indicates the desired page number, when we want paginate - over results - :query per_page: used in conjunction with "page" query string parameter. - indicates the desired page size - :query status: (optional filter) allow us to get active, non active or all - public clouds - :query order_by: (optional) used in conjunction with query string parameter - "order_dir", point out the desired order of the result (date or name) - :query order_dir: (optional) used in conjunction with query string parameter - "order", point out the desired order direction of the result (asc or desc) - - :reqheader Authorization: OAuth 2.0 Bearer Access Token - - :statuscode 200: no error - :statuscode 412: invalid parameters - :statuscode 500: server error - -.. http:get:: /api/v1/marketplace/consultants/(int:id) - - Get desired consultant point out by `id` param - - **Example request**: - - .. sourcecode:: http - - GET /api/v1/marketplace/consultants/123456 HTTP/1.1 - Host: openstackid.org - Accept: application/json, text/javascript - - **Example response**: - - .. sourcecode:: http - - HTTP/1.1 200 OK - Vary: Accept - Content-Type: text/javascript - - { - "ID":"123456", - "Created":"2014-04-23 05:36:10", - "LastEdited":"2015-02-04 11:13:58", - "Name":"Consultant Name", - "Slug":"consultant_name", - "Overview":"lorep ip sum", - "Call2ActionUri":"http:\/\/...", - "Active":"1", - "CompanyID":"123456" - } - - :reqheader Authorization: OAuth 2.0 Bearer Access Token - - :statuscode 200: no error - :statuscode 404: entity not found - :statuscode 500: server error - -.. http:get:: /api/v1/marketplace/consultants/(int:id)/offices - - Get offices locations for consultant pointed out by `id` param - - **Example request**: - - .. sourcecode:: http - - GET /api/v1/marketplace/consultants/123456/offices HTTP/1.1 - Host: openstackid.org - Accept: application/json, text/javascript - - **Example response**: - - .. sourcecode:: http - - HTTP/1.1 200 OK - Vary: Accept - Content-Type: text/javascript - - { - "offices":[ - { - "ID":"45", - "Created":"2014-04-29 16:02:50", - "LastEdited":"2014-04-29 16:02:50", - "Address":null, - "Address2":null, - "State":"CA", - "ZipCode":null, - "City":"Mountain View", - "Country":"US", - "Lat":"37.39", - "Lng":"-122.08" - },... - ] - } - - :reqheader Authorization: OAuth 2.0 Bearer Access Token - - :statuscode 200: no error - :statuscode 404: entity not found (consultant) - :statuscode 500: server error \ No newline at end of file diff --git a/public/js/oauth2/profile/admin/edit-api.js b/public/js/oauth2/profile/admin/edit-api.js index f1215b05..fbbe582d 100644 --- a/public/js/oauth2/profile/admin/edit-api.js +++ b/public/js/oauth2/profile/admin/edit-api.js @@ -171,7 +171,7 @@ $(document).ready(function() { dataType: "json", timeout:60000, success: function (data,textStatus,jqXHR) { - displaySuccessMessage('{{ Lang::get("messages.global_successfully_save_entity", array("entity" => "API")) }}',api_form); + displaySuccessMessage(editApiMessages.success,api_form); }, error: function (jqXHR, textStatus, errorThrown) { ajaxError(jqXHR, textStatus, errorThrown); diff --git a/readme.md b/readme.md index 800150e2..b07fd948 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,8 @@ ## Prerequisites - * LAMP environment + * LAMP/LEMP environment + * Redis * PHP >= 5.4.0 * composer (https://getcomposer.org/) @@ -15,4 +16,8 @@ run following commands on root folder * php artisan migrate --env=YOUR_ENVIRONMENT * php artisan db:seed --env=YOUR_ENVIRONMENT * phpunit --bootstrap vendor/autoload.php + * give proper rights to app/storage folder (775 and proper users) +## Permissions + +Laravel may require some permissions to be configured: folders within storage and vendor require write access by the web server.