Added OAUTH2.0 Admin Groups

now users are able to set admin users on theirs apps
so multiple user could edit/review theirs apps

Change-Id: Ic44bd9363a20d388f81acf2f4ed5c36deb4d4e85
This commit is contained in:
Sebastian Marcet 2016-02-25 17:11:31 -03:00
parent b7eb03974e
commit d2bb5d1be2
36 changed files with 1186 additions and 508 deletions

View File

@ -106,8 +106,9 @@ return array(
'Illuminate\Workbench\WorkbenchServiceProvider',
'Illuminate\Redis\RedisServiceProvider',
'services\utils\UtilsProvider',
'repositories\RepositoriesProvider',
'services\oauth2\OAuth2ServiceProvider',
'repositories\RepositoriesProvider',
'factories\FactoriesProvider',
'services\oauth2\OAuth2ServiceProvider',
'services\openid\OpenIdProvider',
'auth\AuthenticationServiceProvider',
'services\ServicesProvider',
@ -182,5 +183,5 @@ return array(
'View' => 'Illuminate\Support\Facades\View',
'RedisLV4' => 'Illuminate\Support\Facades\Redis',
),
'version' => 'XX.XX.XX',
);

View File

@ -252,13 +252,17 @@ class AdminController extends BaseController {
$friendly_scopes = $this->scope_service->getFriendlyScopesByName(explode(' ',$refresh_token->scope));
$refresh_token->setFriendlyScopes(implode(', ',$friendly_scopes));
}
return View::make("oauth2.profile.edit-user-grants",array(
'user_id' => $user->getId(),
'access_tokens' => $access_tokens ,
'refresh_tokens' => $refresh_tokens ,
"is_oauth2_admin" => $user->isOAuth2ServerAdmin(),
"is_openstackid_admin" => $user->isOpenstackIdAdmin(),
));
return View::make("oauth2.profile.edit-user-grants",
array
(
'user_id' => $user->getId(),
'access_tokens' => $access_tokens ,
'refresh_tokens' => $refresh_tokens ,
'is_oauth2_admin' => $user->isOAuth2ServerAdmin(),
'is_openstackid_admin' => $user->isOpenstackIdAdmin(),
)
);
}
public function listOAuth2Clients(){

View File

@ -66,10 +66,10 @@ final class ApiScopeGroupController extends AbstractRESTController implements IC
{
parent::__construct($log_service);
$this->repository = $repository;
$this->user_repository = $user_repository;
$this->scope_service = $scope_service;
$this->service = $service;
$this->repository = $repository;
$this->user_repository = $user_repository;
$this->scope_service = $scope_service;
$this->service = $service;
$this->allowed_filter_fields = array('');
$this->allowed_projection_fields = array('*');
}
@ -97,7 +97,7 @@ final class ApiScopeGroupController extends AbstractRESTController implements IC
'name' => 'required|text|max:512',
'active' => 'required|boolean',
'scopes' => 'required',
'users' => 'required',
'users' => 'required|user_ids',
);
// Creates a Validator instance and validates the data.
$validation = Validator::make($values, $rules);
@ -204,7 +204,7 @@ final class ApiScopeGroupController extends AbstractRESTController implements IC
'name' => 'required|text|max:512',
'active' => 'required|boolean',
'scopes' => 'required',
'users' => 'required',
'users' => 'required|user_ids',
);
// Creates a Validator instance and validates the data.
$validation = Validator::make($values, $rules);
@ -253,26 +253,4 @@ final class ApiScopeGroupController extends AbstractRESTController implements IC
}
}
public function fetchUsers()
{
$values = Input::all();
if(!isset($values['t'])) return $this->error404();
$term = $values['t'];
$users = $this->user_repository->getByEmailOrName($term);
if(count($users) > 0)
{
$list = array();
foreach($users as $u)
{
array_push($list, array
(
'id' => $u->id,
'value' => sprintf('%s (%s)', $u->getFullName(), $u->getEmail())
)
);
}
return $this->ok($list);
}
return $this->updated();
}
}

View File

@ -8,7 +8,7 @@ use oauth2\services\ITokenService;
use utils\services\ILogService;
use utils\exceptions\EntityNotFoundException;
use oauth2\exceptions\InvalidApiScope;
use utils\services\IAuthService;
/**
* Class ClientApiController
* Client REST API
@ -29,25 +29,25 @@ final class ClientApiController extends AbstractRESTController implements ICRUDC
*/
private $token_service;
/**
* @param IApiScopeService $scope_service
* @param ITokenService $token_service
* @param IClientService $client_service
* @param ILogService $log_service
* @var IAuthService
*/
private $auth_service;
public function __construct
(
IApiScopeService $scope_service,
ITokenService $token_service,
IClientService $client_service,
IAuthService $auth_service,
ILogService $log_service
) {
parent::__construct($log_service);
$this->client_service = $client_service;
$this->scope_service = $scope_service;
$this->token_service = $token_service;
$this->scope_service = $scope_service;
$this->token_service = $token_service;
$this->auth_service = $auth_service;
//set filters allowed values
$this->allowed_filter_fields = array('user_id');
@ -81,7 +81,6 @@ final class ClientApiController extends AbstractRESTController implements ICRUDC
{
try {
$res = $this->client_service->deleteClientByIdentifier($id);
return $res ? $this->deleted() : $this->error404(array('error' => 'operation failed'));
} catch (Exception $ex) {
$this->log_service->error($ex);
@ -101,10 +100,10 @@ final class ClientApiController extends AbstractRESTController implements ICRUDC
// Build the validation constraint set.
$rules = array(
'user_id' => 'required|integer',
'app_name' => 'required|alpha_dash|max:255',
'app_description' => 'required|freetext',
'website' => 'url',
'admin_users' => 'user_ids',
'application_type' => 'required|applicationtype',
);
@ -120,47 +119,30 @@ final class ClientApiController extends AbstractRESTController implements ICRUDC
if ($this->client_service->existClientAppName($values['app_name'])) {
return $this->error400(array('error' => 'application Name already exists!.'));
}
$admin_users = trim($values['admin_users']);
$admin_users = empty($admin_users) ? array():explode(',',$admin_users);
$new_client = $this->client_service->addClient($values['application_type'], intval($values['user_id']),
trim($values['app_name']), trim($values['app_description']), trim($values['website']));
$new_client = $this->client_service->addClient
(
$values['application_type'],
trim($values['app_name']),
trim($values['app_description']),
trim($values['website']),
$admin_users
);
return $this->created(array('client_id' => $new_client->id));
return $this->created
(
array
(
'id' => $new_client->id,
'client_id' => $new_client->client_id,
'client_secret' => $new_client->client_secret,
)
);
} catch (Exception $ex) {
$this->log_service->error($ex);
return $this->error500($ex);
}
}
/**
* @return mixed
*/
public function getByPage()
{
try {
//check for optional filters param on querystring
$fields = $this->getProjection(Input::get('fields', null));
$filters = $this->getFilters(Input::except('fields', 'limit', 'offset'));
$page_nbr = intval(Input::get('offset', 1));
$page_size = intval(Input::get('limit', 10));
$list = $this->client_service->getAll($page_nbr, $page_size, $filters, $fields);
$items = array();
foreach ($list->getItems() as $client) {
$data = $client->toArray();
$data['application_type'] = $client->getFriendlyApplicationType();
array_push($items, $data);
}
return $this->ok(array(
'page' => $items,
'total_items' => $list->getTotal()
));
} catch (Exception $ex) {
$this->log_service->error($ex);
return $this->error500($ex);
}
}
@ -207,6 +189,7 @@ final class ClientApiController extends AbstractRESTController implements ICRUDC
'id_token_signed_response_alg' => 'sometimes|required|signing_alg',
'id_token_encrypted_response_alg' => 'sometimes|required|encrypted_alg',
'id_token_encrypted_response_enc' => 'sometimes|required|encrypted_enc',
'admin_users' => 'user_ids',
);
// Creates a Validator instance and validates the data.
@ -240,6 +223,42 @@ final class ClientApiController extends AbstractRESTController implements ICRUDC
}
}
/**
* @return mixed
*/
public function getByPage()
{
try {
$items = array();
$user = $this->auth_service->getCurrentUser();
$clients = $user->getClients();
foreach ($clients as $client)
{
$data = $client->toArray();
$data['application_type'] = $client->getFriendlyApplicationType();
$data['is_own'] = $client->isOwner($this->auth_service->getCurrentUser());
$data['modified_by'] = $client->getEditedByNice();
array_push($items, $data);
}
return $this->ok
(
array
(
'page' => $items,
'total_items' => count($items)
)
);
}
catch (Exception $ex)
{
$this->log_service->error($ex);
return $this->error500($ex);
}
}
public function addAllowedScope($id, $scope_id)
{
@ -282,12 +301,10 @@ final class ClientApiController extends AbstractRESTController implements ICRUDC
}
}
public function activate($id)
{
try {
$res = $this->client_service->activateClient($id, true);
return $res ? $this->ok() : $this->error404(array('error' => 'operation failed'));
} catch (AbsentClientException $ex1) {
$this->log_service->error($ex1);
@ -317,7 +334,6 @@ final class ClientApiController extends AbstractRESTController implements ICRUDC
}
}
public function regenerateClientSecret($id)
{
try

View File

@ -16,19 +16,38 @@ use utils\services\ILogService;
use openid\services\IUserService;
use oauth2\services\ITokenService;
use oauth2\exceptions\ExpiredAccessTokenException;
use auth\IUserRepository;
/**
* Class UserApiController
*/
class UserApiController extends AbstractRESTController implements ICRUDController {
/**
* @var IUserService
*/
private $user_service;
/**
* @var ITokenService
*/
private $token_service;
public function __construct(ILogService $log_service, IUserService $user_service,ITokenService $token_service){
/**
* @var IUserRepository
*/
private $user_repository;
public function __construct
(
IUserRepository $user_repository,
ILogService $log_service,
IUserService $user_service,
ITokenService $token_service
){
parent::__construct($log_service);
$this->user_service = $user_service;
$this->token_service = $token_service;
$this->user_service = $user_service;
$this->token_service = $token_service;
$this->user_repository = $user_repository;
}
/**
@ -129,4 +148,27 @@ class UserApiController extends AbstractRESTController implements ICRUDControlle
{
// TODO: Implement update() method.
}
public function fetch()
{
$values = Input::all();
if(!isset($values['t'])) return $this->error404();
$term = $values['t'];
$users = $this->user_repository->getByEmailOrName($term);
if(count($users) > 0)
{
$list = array();
foreach($users as $u)
{
array_push($list, array
(
'id' => $u->id,
'value' => sprintf('%s', $u->getFullName())
)
);
}
return $this->ok($list);
}
return $this->updated();
}
}

View File

@ -0,0 +1,47 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddOauthAppAdmins extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('oauth2_client_admin_users', function($table)
{
$table->timestamps();
$table->bigInteger("oauth2_client_id")->unsigned();
$table->index('oauth2_client_id');
$table->foreign('oauth2_client_id')
->references('id')
->on('oauth2_client')
->onDelete('cascade')
->onUpdate('no action'); ;
$table->bigInteger("user_id")->unsigned();
$table->index('user_id');
$table->foreign('user_id')
->references('id')
->on('openid_users')
->onDelete('cascade')
->onUpdate('no action');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('oauth2_client_admin_users');
}
}

View File

@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class UpdateOauth2ClientAdminUsers extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('oauth2_client', function($table) {
$table->bigInteger('edited_by_id')->unsigned()->nullable();
$table->index('edited_by_id');
$table->foreign('edited_by_id')->references('id')->on('openid_users');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('oauth2_client', function($table)
{
$table->dropForeign('edited_by_id');
$table->dropIndex('edited_by_id');
$table->dropColumn('edited_by_id');
});
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace factories;
/**
* Copyright 2015 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App;
use Illuminate\Support\ServiceProvider;
/**
* Class FactoriesProvider
*/
class FactoriesProvider extends ServiceProvider
{
protected $defer = false;
public function boot()
{
}
public function register()
{
App::singleton('oauth2\factories\IOAuth2ClientFactory', 'factories\OAuth2ClientFactory');
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace factories;
use oauth2\factories\IOAuth2ClientFactory;
use oauth2\models\IClient;
use oauth2\OAuth2Protocol;
/**
* Copyright 2015 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
final class OAuth2ClientFactory implements IOAuth2ClientFactory
{
/**
* @param string $app_name
* @param $owner
* @param string $application_type
* @return IClient
*/
public function build($app_name, $owner, $application_type)
{
$client = new \Client
(
array
(
'max_auth_codes_issuance_basis' => 0,
'max_refresh_token_issuance_basis' => 0,
'max_access_token_issuance_qty' => 0,
'max_access_token_issuance_basis' => 0,
'max_refresh_token_issuance_qty' => 0,
'use_refresh_token' => false,
'rotate_refresh_token' => false,
)
);
$client->setOwner($owner);
$client->app_name = $app_name;
$client->active = true;
$client->use_refresh_token = false;
$client->rotate_refresh_token = false;
$client->application_type = $application_type;
if ($client->client_type === IClient::ClientType_Confidential)
{
$client->token_endpoint_auth_method = OAuth2Protocol::TokenEndpoint_AuthMethod_ClientSecretBasic;
}
else
{
$client->token_endpoint_auth_method = OAuth2Protocol::TokenEndpoint_AuthMethod_None;
}
return $client;
}
}

View File

@ -110,6 +110,13 @@ Route::filter('guest', function () {
if (Auth::check()) return Redirect::to('/');
});
Route::filter('user.logged', function () {
if (!Auth::check())
{
return Response::json(array('error' => 'operation not allowed.'), 400);
}
});
/*
|--------------------------------------------------------------------------
| CSRF Protection Filter
@ -158,7 +165,30 @@ Route::filter('user.owns.client.policy',function($route, $request){
$client = $client_service->getClientByIdentifier($client_id);
$user = $authentication_service->getCurrentUser();
if (is_null($client) || intval($client->getUserId()) !== intval($user->getId()))
if (is_null($client) || !$client->isOwner($user))
throw new Exception('invalid client id for current user');
} catch (Exception $ex) {
Log::error($ex);
return Response::json(array('error' => 'operation not allowed.'), 400);
}
});
Route::filter('user.can.edit.client.policy',function($route, $request){
try{
$authentication_service = ServiceLocator::getInstance()->getService(UtilsServiceCatalog::AuthenticationService);
$client_service = ServiceLocator::getInstance()->getService(OAuth2ServiceCatalog::ClientService);
$client_id = $route->getParameter('id');
if(is_null($client_id))
$client_id = $route->getParameter('client_id');
if(is_null($client_id))
$client_id = Input::get('client_id',null);;
$client = $client_service->getClientByIdentifier($client_id);
$user = $authentication_service->getCurrentUser();
if (is_null($client) || !$client->candEdit($user))
throw new Exception('invalid client id for current user');
} catch (Exception $ex) {

View File

@ -217,7 +217,14 @@ class User extends BaseModelEloquent implements UserInterface, IOpenIdUser, IOAu
public function getClients()
{
return $this->clients()->get();
$own_clients = $this->clients()->get();
$managed_clients = $this->managed_clients()->get();
return $own_clients->merge($managed_clients);
}
public function managed_clients()
{
return $this->belongsToMany('Client', 'oauth2_client_admin_users', 'user_id', 'oauth2_client_id');
}
/**

View File

@ -0,0 +1,32 @@
<?php
/**
* Copyright 2015 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
namespace oauth2\factories;
use oauth2\models\IClient;
/**
* Interface IOAuth2ClientFactory
* @package oauth2\factories
*/
interface IOAuth2ClientFactory
{
/**
* @param string $app_name
* @param $owner
* @param string $application_type
* @return IClient
*/
public function build($app_name, $owner, $application_type);
}

View File

@ -256,4 +256,39 @@ interface IClient
*/
public function isPostLogoutUriAllowed($post_logout_uri);
/**
* @param $user
*/
public function candEdit($user);
/**
* @param $user
* @return bool
*/
public function canDelete($user);
/**
* @param $user
* @return $this
*/
public function setOwner($user);
/**
* @return $this
*/
public function removeAllScopes();
/**
* @param IApiScope $scope
* @return $this
*/
public function addScope(IApiScope $scope);
/**
* @param $editing_user
* @return $this
*/
public function setEditedBy($editing_user);
}

View File

@ -2,10 +2,12 @@
namespace oauth2\services;
use oauth2\exceptions\AbsentClientException;
use oauth2\exceptions\InvalidClientAuthMethodException;
use oauth2\exceptions\MissingClientAuthorizationInfo;
use oauth2\models\ClientAuthenticationContext;
use oauth2\models\IClient;
use openid\model\IOpenIdUser;
/**
* Interface IClientService
* @package oauth2\services
@ -35,25 +37,33 @@ interface IClientService {
public function getClientByIdentifier($id);
/**
* Creates a new client
* @param string $application_type
* @param int $user_id
* @param string $app_name
* @param string $app_description
* @param null|string $app_url
* @param string $app_logo
* @param string $application_type
* @param string $app_name
* @param string $app_description
* @param null|string $app_url
* @param array $admin_users
* @param string $app_logo
* @return IClient
*/
public function addClient
(
$application_type,
$user_id,
$app_name,
$app_description,
$app_url = null,
array $admin_users = array(),
$app_logo = ''
);
/**
* @param $id
* @param array $params
* @throws AbsentClientException
* @throws \ValidationException
* @return mixed
*/
public function update($id, array $params);
/**
* @param $id
* @param $scope_id
@ -139,20 +149,6 @@ interface IClientService {
*/
public function getAll($page_nbr=1,$page_size=10,array $filters=array(), array $fields=array('*'));
/**
* @param IClient $client
* @return bool
*/
public function save(IClient $client);
/**
* @param $id
* @param array $params
* @return bool
* @throws \oauth2\exceptions\InvalidClientException
*/
public function update($id, array $params);
/**
* @param string $origin
* @return IClient

View File

@ -39,6 +39,7 @@ class ApiScopeGroup extends BaseModelEloquent implements IEntity
public function addScope(IApiScope $scope)
{
$this->scopes()->attach($scope->id);
return $this;
}
/**
@ -47,6 +48,7 @@ class ApiScopeGroup extends BaseModelEloquent implements IEntity
public function addUser(IOAuth2User $user)
{
$this->users()->attach($user->id);
return $this;
}
/**
@ -55,6 +57,13 @@ class ApiScopeGroup extends BaseModelEloquent implements IEntity
public function removeScope(IOAuth2User $scope)
{
$this->scopes()->detach($scope->id);
return $this;
}
public function removeAllScopes()
{
$this->scopes()->detach();
return $this;
}
/**
@ -63,6 +72,13 @@ class ApiScopeGroup extends BaseModelEloquent implements IEntity
public function removeUser(IOAuth2User $user)
{
$this->users()->detach($user->id);
return $this;
}
public function removeAllUsers()
{
$this->users()->detach();
return $this;
}
/**

View File

@ -8,7 +8,7 @@ use oauth2\models\IClientPublicKey;
use oauth2\models\JWTResponseInfo;
use oauth2\models\TokenEndpointAuthInfo;
use utils\model\BaseModelEloquent;
use oauth2\models\IApiScope;
/**
* Class Client
*/
@ -61,7 +61,8 @@ class Client extends BaseModelEloquent implements IClient
'id_token_encrypted_response_alg',
'id_token_encrypted_response_enc',
'redirect_uris',
'allowed_origins'
'allowed_origins',
'edited_by_id',
);
public static $valid_app_types = array
@ -103,6 +104,14 @@ class Client extends BaseModelEloquent implements IClient
return $this->hasMany('ClientPublicKey','oauth2_client_id','id');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function admin_users()
{
return $this->belongsToMany('auth\User','oauth2_client_admin_users','oauth2_client_id','user_id');
}
/**
* @param string $value
*/
@ -129,7 +138,6 @@ class Client extends BaseModelEloquent implements IClient
}
}
public function access_tokens()
{
return $this->hasMany('AccessToken');
@ -145,6 +153,11 @@ class Client extends BaseModelEloquent implements IClient
return $this->belongsTo('auth\User');
}
public function edited_by()
{
return $this->belongsTo('auth\User','edited_by_id');
}
public function resource_server()
{
return $this->belongsTo('ResourceServer');
@ -155,6 +168,15 @@ class Client extends BaseModelEloquent implements IClient
return $this->belongsToMany('ApiScope','oauth2_client_api_scope','client_id','scope_id');
}
/**
* @return $this
*/
public function removeAllScopes()
{
$this->scopes()->detach();
return $this;
}
public function getClientId()
{
return $this->client_id;
@ -586,4 +608,89 @@ class Client extends BaseModelEloquent implements IClient
}
return false;
}
/**
* @param mixed $user
*/
public function addAdminUser($user)
{
$this->admin_users()->attach($user->id);
return $this;
}
/**
* @param mixed $user
*/
public function removeAdminUser($user)
{
$this->admin_users()->detach($user->id);
return $this;
}
public function removeAllAdminUsers(){
$this->admin_users()->detach();
return $this;
}
/**
* @param $user
* @return bool
*/
public function candEdit($user)
{
$is_admin = $this->admin_users()->where('id', '=', $user->id)->count() > 0;
$is_owner = intval($this->user_id) === intval($user->id);
return $is_owner || $is_admin;
}
/**
* @param $user
* @return bool
*/
public function canDelete($user)
{
return $this->isOwner($user);
}
public function isOwner($user)
{
return intval($this->user_id) === intval($user->id);
}
public function setOwner($user)
{
$this->user()->associate($user);
return $this;
}
/**
* @param IApiScope $scope
* @return $this
*/
public function addScope(IApiScope $scope)
{
$this->scopes()->attach($scope->id);
return $this;
}
/**
* @param $editing_user
* @return $this
*/
public function setEditedBy($editing_user){
$this->edited_by()->associate($editing_user);
return $this;
}
public function getEditedByNice()
{
$user = $this->edited_by()->first();
return is_null($user)? 'N/A':$user->getEmail();
}
public function getOwnerNice()
{
$user = $this->user()->first();
return is_null($user)? 'N/A':$user->getEmail();
}
}

View File

@ -40,7 +40,7 @@ final class EloquentClientRepository implements IClientRepository {
* @param ILogService $log_service
*/
public function __construct(Client $client, ILogService $log_service){
$this->client = $client;
$this->client = $client;
$this->log_service = $log_service;
}

View File

@ -80,14 +80,11 @@ Route::group(array("before" => array("ssl", "auth")), function () {
Route::group(array('prefix' => 'admin', 'before' => 'ssl|auth'), function () {
//client admin UI
Route::get('clients/edit/{id}',
array('before' => 'oauth2.enabled|user.owns.client.policy', 'uses' => 'AdminController@editRegisteredClient'));
Route::get('clients/edit/{id}', array('before' => 'oauth2.enabled|user.can.edit.client.policy', 'uses' => 'AdminController@editRegisteredClient'));
Route::get('clients', array('before' => 'oauth2.enabled', 'uses' => 'AdminController@listOAuth2Clients'));
Route::get('/grants', array('before' => 'oauth2.enabled', 'uses' => 'AdminController@editIssuedGrants'));
//oauth2 server admin UI
Route::group(array('before' => 'oauth2.enabled|oauth2.server.admin'), function () {
Route::get('/api-scope-groups', 'AdminController@listApiScopeGroups');
Route::get('/api-scope-groups/{id}', 'AdminController@editApiScopeGroup');
Route::get('/resource-servers', 'AdminController@listResourceServers');
@ -111,10 +108,9 @@ Route::group(array('prefix' => 'admin', 'before' => 'ssl|auth'), function () {
//Admin Backend API
Route::group(array('prefix' => 'admin/api/v1', 'before' => 'ssl|auth'), function () {
Route::group(array('prefix' => 'users'), function () {
Route::delete('/{id}/locked',
array('before' => 'openstackid.server.admin.json', 'uses' => 'UserApiController@unlock'));
Route::delete('/{id}/token/{value}',
array('before' => 'is.current.user', 'uses' => 'UserApiController@revokeToken'));
Route::delete('/{id}/locked',array('before' => 'openstackid.server.admin.json', 'uses' => 'UserApiController@unlock'));
Route::delete('/{id}/token/{value}', array('before' => 'is.current.user', 'uses' => 'UserApiController@revokeToken'));
Route::get('/fetch', array('before' => 'user.logged', 'uses' => "UserApiController@fetch"));
});
Route::group(array('prefix' => 'banned-ips', 'before' => 'openstackid.server.admin.json'), function () {
@ -127,34 +123,34 @@ Route::group(array('prefix' => 'admin/api/v1', 'before' => 'ssl|auth'), function
Route::group(array('prefix' => 'clients'), function () {
// public keys
Route::post('/{id}/public_keys', array('before' => 'user.owns.client.policy', 'uses' => 'ClientPublicKeyApiController@create'));
Route::get('/{id}/public_keys', array('before' => 'user.owns.client.policy', 'uses' => 'ClientPublicKeyApiController@getByPage'));
Route::delete('/{id}/public_keys/{public_key_id}', array('before' => 'user.owns.client.policy', 'uses' => 'ClientPublicKeyApiController@delete'));
Route::put('/{id}/public_keys/{public_key_id}', array('before' => 'user.owns.client.policy', 'uses' => 'ClientPublicKeyApiController@update'));
Route::post('/{id}/public_keys', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientPublicKeyApiController@create'));
Route::get('/{id}/public_keys', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientPublicKeyApiController@getByPage'));
Route::delete('/{id}/public_keys/{public_key_id}', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientPublicKeyApiController@delete'));
Route::put('/{id}/public_keys/{public_key_id}', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientPublicKeyApiController@update'));
Route::post('/', array('before' => 'is.current.user', 'uses' => 'ClientApiController@create'));
Route::put('/', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@update'));
Route::put('/', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientApiController@update'));
Route::get('/{id}', "ClientApiController@get");
Route::get('/', array('before' => 'is.current.user', 'uses' => 'ClientApiController@getByPage'));
Route::delete('/{id}', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@delete'));
//allowed redirect uris endpoints
Route::get('/{id}/uris', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@getRegisteredUris'));
Route::post('/{id}/uris', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@addAllowedRedirectUri'));
Route::delete('/{id}/uris/{uri_id}', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@deleteClientAllowedUri'));
Route::get('/{id}/uris', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientApiController@getRegisteredUris'));
Route::post('/{id}/uris', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientApiController@addAllowedRedirectUri'));
Route::delete('/{id}/uris/{uri_id}', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientApiController@deleteClientAllowedUri'));
//allowed origin endpoints endpoints
Route::get('/{id}/origins', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@geAllowedOrigins'));
Route::post('/{id}/origins', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@addAllowedOrigin'));
Route::delete('/{id}/origins/{origin_id}', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@deleteClientAllowedOrigin'));
Route::get('/{id}/origins', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientApiController@geAllowedOrigins'));
Route::post('/{id}/origins', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientApiController@addAllowedOrigin'));
Route::delete('/{id}/origins/{origin_id}', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientApiController@deleteClientAllowedOrigin'));
Route::delete('/{id}/lock', array('before' => 'openstackid.server.admin.json', 'uses' => 'ClientApiController@unlock'));
Route::put('/{id}/secret', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@regenerateClientSecret'));
Route::put('/{id}/use-refresh-token', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@setRefreshTokenClient'));
Route::put('/{id}/rotate-refresh-token', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@setRotateRefreshTokenPolicy'));
Route::get('/{id}/access-token', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@getAccessTokens'));
Route::get('/{id}/refresh-token', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@getRefreshTokens'));
Route::delete('/{id}/token/{value}/{hint}', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@revokeToken'));
Route::put('/{id}/scopes/{scope_id}', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@addAllowedScope'));
Route::delete('/{id}/scopes/{scope_id}', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@removeAllowedScope'));
Route::put('/{id}/use-refresh-token', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientApiController@setRefreshTokenClient'));
Route::put('/{id}/rotate-refresh-token', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientApiController@setRotateRefreshTokenPolicy'));
Route::get('/{id}/access-token', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientApiController@getAccessTokens'));
Route::get('/{id}/refresh-token', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientApiController@getRefreshTokens'));
Route::delete('/{id}/token/{value}/{hint}', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientApiController@revokeToken'));
Route::put('/{id}/scopes/{scope_id}', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientApiController@addAllowedScope'));
Route::delete('/{id}/scopes/{scope_id}', array('before' => 'user.can.edit.client.policy', 'uses' => 'ClientApiController@removeAllowedScope'));
Route::put('/{id}/active', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@activate'));
Route::delete('/{id}/active', array('before' => 'user.owns.client.policy', 'uses' => 'ClientApiController@deactivate'));
@ -181,7 +177,6 @@ Route::group(array('prefix' => 'admin/api/v1', 'before' => 'ssl|auth'), function
Route::delete('/{id}', "ApiScopeGroupController@delete");
Route::put('/{id}/active', "ApiScopeGroupController@activate");
Route::delete('/{id}/active', "ApiScopeGroupController@deactivate");
Route::get('/users', "ApiScopeGroupController@fetchUsers");
});
// apis

View File

@ -116,8 +116,7 @@ final class ApiScopeGroupService implements IApiScopeGroupService
}
}
else if($param === 'users'){
$ids = $group->users()->getRelatedIds();
$group->users()->detach($ids);
$group->removeAllUsers();
$users = explode(',', $params['users']);
foreach($users as $user_id)
{

View File

@ -23,6 +23,7 @@ use ClientPublicKey;
use oauth2\repositories\IClientRepository;
use DB;
use ValidationException;
use utils\services\IAuthService;
/**
* Class ClienPublicKeyService
* @package services\oauth2
@ -35,13 +36,20 @@ final class ClienPublicKeyService extends AssymetricKeyService implements IClien
*/
private $client_repository;
/**
* @var IAuthService
*/
private $auth_service;
public function __construct(
IClientPublicKeyRepository $repository,
IClientRepository $client_repository,
ITransactionService $tx_service)
IClientRepository $client_repository,
IAuthService $auth_service,
ITransactionService $tx_service
)
{
$this->client_repository = $client_repository;
$this->auth_service = $auth_service;
parent::__construct($repository, $tx_service);
}
@ -52,9 +60,9 @@ final class ClienPublicKeyService extends AssymetricKeyService implements IClien
public function register(array $params)
{
$client_repository = $this->client_repository;
$repository = $this->repository;
return $this->tx_service->transaction(function() use($params, $repository, $client_repository)
$repository = $this->repository;
$auth_service = $this->auth_service;
return $this->tx_service->transaction(function() use($params, $repository, $client_repository, $auth_service)
{
if ($repository->getByPEM($params['pem_content']))
@ -95,6 +103,8 @@ final class ClienPublicKeyService extends AssymetricKeyService implements IClien
);
$client->addPublicKey($public_key);
$client->setEditedBy($auth_service->getCurrentUser());
$client_repository->add($client);
return $public_key;
});
}

View File

@ -2,6 +2,7 @@
namespace services\oauth2;
use auth\IUserRepository;
use Client;
use DB;
use Event;
@ -21,13 +22,15 @@ use oauth2\services\IApiScopeService;
use oauth2\services\IClientCrendentialGenerator;
use oauth2\services\IClientService;
use oauth2\services\id;
use Request;
use URL\Normalizer;
use utils\db\ITransactionService;
use utils\exceptions\EntityNotFoundException;
use utils\http\HttpUtils;
use utils\services\IAuthService;
use openid\model\IOpenIdUser;
use oauth2\repositories\IClientRepository;
use oauth2\factories\IOAuth2ClientFactory;
use Request;
/**
* Class ClientService
* @package services\oauth2
@ -43,28 +46,42 @@ class ClientService implements IClientService
*/
private $scope_service;
/**
* @var IUserRepository
*/
private $user_repository;
/**
* @var IClientCrendentialGenerator
*/
private $client_credential_generator;
/**
* @param IAuthService $auth_service
* @param IApiScopeService $scope_service
* @param IClientCrendentialGenerator $client_credential_generator
* @param ITransactionService $tx_service
* @var IClientRepository
*/
private $client_repository;
/**
* @var IOAuth2ClientFactory
*/
private $client_factory;
public function __construct
(
IUserRepository $user_repository,
IClientRepository $client_repository,
IAuthService $auth_service,
IApiScopeService $scope_service,
IClientCrendentialGenerator $client_credential_generator,
IOAuth2ClientFactory $client_factory,
ITransactionService $tx_service
)
{
$this->auth_service = $auth_service;
$this->user_repository = $user_repository;
$this->scope_service = $scope_service;
$this->client_credential_generator = $client_credential_generator;
$this->client_repository = $client_repository;
$this->client_factory = $client_factory;
$this->tx_service = $tx_service;
}
@ -141,55 +158,54 @@ class ClientService implements IClientService
throw new InvalidClientAuthMethodException;
}
public function addClient($application_type, $user_id, $app_name, $app_description, $app_url = null, $app_logo = '')
/**
* @param string $application_type
* @param string $app_name
* @param string $app_description
* @param null|string $app_url
* @param array $admin_users
* @param string $app_logo
* @return IClient
*/
public function addClient
(
$application_type,
$app_name,
$app_description,
$app_url = null,
array $admin_users = array(),
$app_logo = ''
)
{
$scope_service = $this->scope_service;
$scope_service = $this->scope_service;
$client_credential_generator = $this->client_credential_generator;
$user_repository = $this->user_repository;
$client_repository = $this->client_repository;
$client_factory = $this->client_factory;
$current_user = $this->auth_service->getCurrentUser();
return $this->tx_service->transaction(function () use (
$application_type,
$user_id,
$current_user,
$app_name,
$app_url,
$app_description,
$app_logo,
$admin_users,
$scope_service,
$user_repository,
$client_repository,
$client_factory,
$client_credential_generator
) {
$client = new Client
(
array
(
'max_auth_codes_issuance_basis' => 0,
'max_refresh_token_issuance_basis' => 0,
'max_access_token_issuance_qty' => 0,
'max_access_token_issuance_basis' => 0,
'max_refresh_token_issuance_qty' => 0,
'use_refresh_token' => false,
'rotate_refresh_token' => false,
)
);
$client->app_name = $app_name;
$client->app_logo = $app_logo;
$client->app_description = $app_description;
$client->application_type = $application_type;
$client = $client_factory->build($app_name,$current_user, $application_type);
$client = $client_credential_generator->generate($client);
if ($client->client_type === IClient::ClientType_Confidential) {
$client->token_endpoint_auth_method = OAuth2Protocol::TokenEndpoint_AuthMethod_ClientSecretBasic;
} else {
$client->token_endpoint_auth_method = OAuth2Protocol::TokenEndpoint_AuthMethod_None;
}
$client->user_id = $user_id;
$client->active = true;
$client->use_refresh_token = false;
$client->rotate_refresh_token = false;
$client->website = $app_url;
$client->Save();
$client->app_logo = $app_logo;
$client->app_description = $app_description;
$client->website = $app_url;
$client_repository->add($client);
//add default scopes
$default_scopes = $scope_service->getDefaultScopes();
@ -204,259 +220,44 @@ class ClientService implements IClientService
) {
continue;
}
$client->scopes()->attach($default_scope->id);
$client->addScope($default_scope);
}
//add admin users
foreach($admin_users as $user_id)
{
$user = $user_repository->get(intval($user_id));
if(is_null($user)) throw new EntityNotFoundException(sprintf('user %s not found.',$user_id));
$client->addAdminUser($user);
}
return $client;
});
}
public function addClientScope($id, $scope_id)
{
$client = Client::find($id);
if (is_null($client)) {
throw new EntityNotFoundException(sprintf("client id %s not found!.", $id));
}
$scope = $this->scope_service->get(intval($scope_id));
if(is_null($scope)) throw new EntityNotFoundException(sprintf("scope %s not found!.", $scope_id));
$user = $client->user()->first();
if($scope->isAssignableByGroups()) {
$allowed = false;
foreach($user->getGroupScopes() as $group_scope)
{
if(intval($group_scope->id) === intval($scope_id))
{
$allowed = true; break;
}
}
if(!$allowed) throw new InvalidApiScope(sprintf('you cant assign to this client api scope %s', $scope_id));
}
if($scope->isSystem() && !$user->canUseSystemScopes())
throw new InvalidApiScope(sprintf('you cant assign to this client api scope %s', $scope_id));
return $client->scopes()->attach($scope_id);
}
public function deleteClientScope($id, $scope_id)
{
$client = Client::find($id);
if (is_null($client)) {
throw new AbsentClientException(sprintf("client id %s does not exists!", $id));
}
return $client->scopes()->detach($scope_id);
}
public function deleteClientByIdentifier($id)
{
$res = false;
$this->tx_service->transaction(function () use ($id, &$res) {
$client = Client::find($id);
if (!is_null($client)) {
$client->scopes()->detach();
Event::fire('oauth2.client.delete', array($client->client_id));
$res = $client->delete();
}
});
return $res;
}
/**
* Regenerates Client Secret
* @param $id client id
* @return IClient
*/
public function regenerateClientSecret($id)
{
$client_credential_generator = $this->client_credential_generator;
return $this->tx_service->transaction(function () use ($id, $client_credential_generator)
{
$client = Client::find($id);
if (is_null($client))
{
throw new AbsentClientException(sprintf("client id %d does not exists!.", $id));
}
if ($client->client_type != IClient::ClientType_Confidential)
{
throw new InvalidClientType
(
sprintf
(
"client id %d is not confidential type!.",
$id
)
);
}
$client_credential_generator->generate($client, true)->save();
Event::fire('oauth2.client.regenerate.secret', array($client->client_id));
return $client;
});
}
/**
* @param client $client_id
* @return mixed
* @throws \oauth2\exceptions\AbsentClientException
*/
public function lockClient($client_id)
{
$res = false;
$this_var = $this;
$this->tx_service->transaction(function () use ($client_id, &$res, &$this_var) {
$client = $this_var->getClientByIdentifier($client_id);
if (is_null($client)) {
throw new AbsentClientException($client_id, sprintf("client id %s does not exists!", $client_id));
}
$client->locked = true;
$res = $client->Save();
});
return $res;
}
/**
* @param client $client_id
* @return mixed
* @throws \oauth2\exceptions\AbsentClientException
*/
public function unlockClient($client_id)
{
$res = false;
$this_var = $this;
$this->tx_service->transaction(function () use ($client_id, &$res, &$this_var) {
$client = $this_var->getClientByIdentifier($client_id);
if (is_null($client)) {
throw new AbsentClientException($client_id, sprintf("client id %s does not exists!", $client_id));
}
$client->locked = false;
$res = $client->Save();
});
return $res;
}
/**
* @param $client_id
* @return IClient
*/
public function getClientById($client_id)
{
$client = Client::where('client_id', '=', $client_id)->first();
return $client;
}
public function activateClient($id, $active)
{
$client = $this->getClientByIdentifier($id);
if (is_null($client)) {
throw new AbsentClientException(sprintf("client id %s does not exists!", $id));
}
$client->active = $active;
return $client->Save();
}
public function getClientByIdentifier($id)
{
$client = Client::where('id', '=', $id)->first();
return $client;
}
public function setRefreshTokenUsage($id, $use_refresh_token)
{
$client = $this->getClientByIdentifier($id);
if (is_null($client)) {
throw new AbsentClientException(sprintf("client id %s does not exists!", $id));
}
$client->use_refresh_token = $use_refresh_token;
return $client->Save();
}
public function setRotateRefreshTokenPolicy($id, $rotate_refresh_token)
{
$client = $this->getClientByIdentifier($id);
if (is_null($client)) {
throw new AbsentClientException(sprintf("client id %s does not exists!", $id));
}
$client->rotate_refresh_token = $rotate_refresh_token;
return $client->Save();
}
public function existClientAppName($app_name)
{
return Client::where('app_name', '=', $app_name)->count() > 0;
}
/**
* gets an api scope by id
* @param $id id of api scope
* @return IApiScope
*/
public function get($id)
{
return Client::find($id);
}
/**
* @param int $page_nbr
* @param int $page_size
* @param array $filters
* @param array $fields
* @return mixed
*/
public function getAll($page_nbr = 1, $page_size = 10, array $filters = array(), array $fields = array('*'))
{
DB::getPaginator()->setCurrentPage($page_nbr);
return Client::Filter($filters)->paginate($page_size, $fields);
}
/**
* @param IClient $client
* @return bool
*/
public function save(IClient $client)
{
if (!$client->exists() || count($client->getDirty()) > 0) {
return $client->Save();
}
return true;
}
/**
* @param $id
* @param array $params
* @return bool
* @throws \oauth2\exceptions\AbsentClientException
* @throws AbsentClientException
* @throws \ValidationException
* @return mixed
*/
public function update($id, array $params)
{
$this_var = $this;
$this_var = $this;
$client_repository = $this->client_repository;
$user_repository = $this->user_repository;
$editing_user = $this->auth_service->getCurrentUser();
return $this->tx_service->transaction(function () use ($id, $params, &$this_var) {
return $this->tx_service->transaction(function () use ($id, $editing_user, $params, $client_repository, $user_repository, &$this_var) {
$client = $client_repository->get($id);
$client = Client::find($id);
if (is_null($client)) {
throw new AbsentClientException(sprintf('client id %s does not exists.', $id));
}
$current_app_type = $client->getApplicationType();
if($current_app_type !== $params['application_type'])
{
@ -558,6 +359,7 @@ class ClientService implements IClientService
'id_token_encrypted_response_enc',
'redirect_uris',
'allowed_origins',
'admin_users',
);
$fields_to_uri_normalize = array
@ -574,32 +376,269 @@ class ClientService implements IClientService
foreach ($allowed_update_params as $param)
{
if (array_key_exists($param, $params))
{
if(in_array($param, $fields_to_uri_normalize))
{
$urls = $params[$param];
if(!empty($urls))
if($param === 'admin_users'){
$admin_users = trim($params['admin_users']);
$admin_users = empty($admin_users) ? array():explode(',',$admin_users);
$client->removeAllAdminUsers();
foreach($admin_users as $user_id)
{
$urls = explode(',', $urls);
$normalized_uris = '';
foreach ($urls as $url) {
$un = new Normalizer($url);
$url = $un->normalize();
if (!empty($normalized_uris)) {
$normalized_uris .= ',';
}
$normalized_uris .= $url;
}
$params[$param] = $normalized_uris;
$user = $user_repository->get(intval($user_id));
if(is_null($user)) throw new EntityNotFoundException(sprintf('user %s not found.',$user_id));
$client->addAdminUser($user);
}
}
$client->{$param} = $params[$param];
else {
if (in_array($param, $fields_to_uri_normalize)) {
$urls = $params[$param];
if (!empty($urls)) {
$urls = explode(',', $urls);
$normalized_uris = '';
foreach ($urls as $url) {
$un = new Normalizer($url);
$url = $un->normalize();
if (!empty($normalized_uris)) {
$normalized_uris .= ',';
}
$normalized_uris .= $url;
}
$params[$param] = $normalized_uris;
}
}
$client->{$param} = $params[$param];
}
}
}
$client_repository->add($client->setEditedBy($editing_user));
return $client;
});
}
public function addClientScope($id, $scope_id)
{
$client = Client::find($id);
if (is_null($client)) {
throw new EntityNotFoundException(sprintf("client id %s not found!.", $id));
}
$scope = $this->scope_service->get(intval($scope_id));
if(is_null($scope)) throw new EntityNotFoundException(sprintf("scope %s not found!.", $scope_id));
$user = $client->user()->first();
if($scope->isAssignableByGroups()) {
$allowed = false;
foreach($user->getGroupScopes() as $group_scope)
{
if(intval($group_scope->id) === intval($scope_id))
{
$allowed = true; break;
}
}
return $this_var->save($client);
if(!$allowed) throw new InvalidApiScope(sprintf('you cant assign to this client api scope %s', $scope_id));
}
if($scope->isSystem() && !$user->canUseSystemScopes())
throw new InvalidApiScope(sprintf('you cant assign to this client api scope %s', $scope_id));
$client->scopes()->attach($scope_id);
$client->setEditedBy($this->auth_service->getCurrentUser());
$client->save();
return $client;
}
public function deleteClientScope($id, $scope_id)
{
$client = Client::find($id);
if (is_null($client)) {
throw new AbsentClientException(sprintf("client id %s does not exists!", $id));
}
$client->scopes()->detach($scope_id);
$client->setEditedBy($this->auth_service->getCurrentUser());
$client->save();
return $client;
}
public function deleteClientByIdentifier($id)
{
$res = false;
$this->tx_service->transaction(function () use ($id, &$res) {
$client = Client::find($id);
if (!is_null($client)) {
$client->scopes()->detach();
Event::fire('oauth2.client.delete', array($client->client_id));
$res = $client->delete();
}
});
return $res;
}
/**
* Regenerates Client Secret
* @param $id client id
* @return IClient
*/
public function regenerateClientSecret($id)
{
$client_credential_generator = $this->client_credential_generator;
$current_user = $this->auth_service->getCurrentUser();
return $this->tx_service->transaction(function () use ($id, $current_user, $client_credential_generator)
{
$client = Client::find($id);
if (is_null($client))
{
throw new AbsentClientException(sprintf("client id %d does not exists!.", $id));
}
if ($client->client_type != IClient::ClientType_Confidential)
{
throw new InvalidClientType
(
sprintf
(
"client id %d is not confidential type!.",
$id
)
);
}
$client = $client_credential_generator->generate($client, true);
$client->setEditedBy($current_user);
$client->save();
Event::fire('oauth2.client.regenerate.secret', array($client->client_id));
return $client;
});
}
/**
* @param client $client_id
* @return mixed
* @throws \oauth2\exceptions\AbsentClientException
*/
public function lockClient($client_id)
{
$res = false;
$this_var = $this;
$this->tx_service->transaction(function () use ($client_id, &$res, &$this_var) {
$client = $this_var->getClientByIdentifier($client_id);
if (is_null($client)) {
throw new AbsentClientException($client_id, sprintf("client id %s does not exists!", $client_id));
}
$client->locked = true;
$res = $client->Save();
});
return $res;
}
/**
* @param client $client_id
* @return mixed
* @throws \oauth2\exceptions\AbsentClientException
*/
public function unlockClient($client_id)
{
$res = false;
$this_var = $this;
$this->tx_service->transaction(function () use ($client_id, &$res, &$this_var) {
$client = $this_var->getClientByIdentifier($client_id);
if (is_null($client)) {
throw new AbsentClientException($client_id, sprintf("client id %s does not exists!", $client_id));
}
$client->locked = false;
$res = $client->Save();
});
return $res;
}
/**
* @param $client_id
* @return IClient
*/
public function getClientById($client_id)
{
$client = Client::where('client_id', '=', $client_id)->first();
return $client;
}
public function activateClient($id, $active)
{
$client = $this->getClientByIdentifier($id);
if (is_null($client)) {
throw new AbsentClientException(sprintf("client id %s does not exists!", $id));
}
$client->active = $active;
return $client->Save();
}
public function getClientByIdentifier($id)
{
$client = Client::where('id', '=', $id)->first();
return $client;
}
public function setRefreshTokenUsage($id, $use_refresh_token)
{
$client = $this->getClientByIdentifier($id);
if (is_null($client)) {
throw new AbsentClientException(sprintf("client id %s does not exists!", $id));
}
$client->use_refresh_token = $use_refresh_token;
$client->setEditedBy($this->auth_service->getCurrentUser());
return $client->Save();
}
public function setRotateRefreshTokenPolicy($id, $rotate_refresh_token)
{
$client = $this->getClientByIdentifier($id);
if (is_null($client)) {
throw new AbsentClientException(sprintf("client id %s does not exists!", $id));
}
$client->rotate_refresh_token = $rotate_refresh_token;
$client->setEditedBy($this->auth_service->getCurrentUser());
return $client->Save();
}
public function existClientAppName($app_name)
{
return Client::where('app_name', '=', $app_name)->count() > 0;
}
/**
* gets an api scope by id
* @param $id id of api scope
* @return IApiScope
*/
public function get($id)
{
return Client::find($id);
}
/**
* @param int $page_nbr
* @param int $page_size
* @param array $filters
* @param array $fields
* @return mixed
*/
public function getAll($page_nbr = 1, $page_size = 10, array $filters = array(), array $fields = array('*'))
{
DB::getPaginator()->setCurrentPage($page_nbr);
return Client::Filter($filters)->paginate($page_size, $fields);
}
/**

View File

@ -338,4 +338,14 @@ class CustomValidator extends Validator
if(is_array($value)) $value = $value[0];
return in_array($value, $valid_values);
}
public function validateUserIds($attribute, $value, $parameters)
{
$user_ids = explode(',',$value);
foreach($user_ids as $id)
{
if(!intval($id)) return false;
}
return true;
}
}

View File

@ -8,6 +8,7 @@
{{ HTML::style('assets/css/main.css') }}
{{ HTML::style('bower_assets/jquery-ui/themes/ui-darkness/jquery-ui.css') }}
{{ HTML::style('bower_assets/fontawesome/css/font-awesome.min.css') }}
{{ HTML::style('bower_assets/sweetalert/dist/sweetalert.css') }}
@yield('css')
</head>
<body>
@ -31,10 +32,12 @@
{{ HTML::script('bower_assets/jquery-validate/dist/additional-methods.min.js')}}
{{ HTML::script('bower_assets/pure-templates/libs/pure.min.js')}}
{{ HTML::script('bower_assets/uri.js/src/URI.min.js')}}
{{ HTML::script('bower_assets/sweetalert/dist/sweetalert.min.js')}}
{{ HTML::script('assets/js/ajax.utils.js')}}
{{ HTML::script('assets/js/jquery.cleanform.js')}}
{{ HTML::script('assets/js/jquery.serialize.js')}}
{{ HTML::script('assets/js/jquery.validate.additional.custom.methods.js')}}
@yield('scripts')
<span class="version hidden">{{ Config::get('app.version') }}</span>
</body>
</html>

View File

@ -27,7 +27,12 @@ Service Account : The OpenstackId OAuth 2.0 Authorization Server supports server
<option value="SERVICE">Service Account</option>
<option value="NATIVE">Native Application</option>
</select>
</div>
</div>
<div class="form-group">
<label class="control-label" for="admin_users">Admin Users&nbsp;<span class="glyphicon glyphicon-info-sign accordion-toggle" aria-hidden="true" title="Choose which users would be administrator of this application"></span></label>
<input type="text" class="form-control" name="admin_users" id="admin_users">
</div>
<div class="checkbox">
<label>

View File

@ -74,7 +74,7 @@
activate : '{{ URL::action("ApiScopeGroupController@activate",array("id"=>"@id")) }}',
deactivate : '{{ URL::action("ApiScopeGroupController@deactivate",array("id"=>"@id")) }}',
add : '{{URL::action("ApiScopeGroupController@create",null)}}',
fetchUsers: '{{URL::action("ApiScopeGroupController@fetchUsers",null)}}'
fetchUsers: '{{URL::action("UserApiController@fetch",null)}}'
};
var all_scopes = [];

View File

@ -56,7 +56,7 @@
activate : '{{ URL::action("ApiScopeGroupController@activate",array("id"=>"@id")) }}',
deactivate : '{{ URL::action("ApiScopeGroupController@deactivate",array("id"=>"@id")) }}',
add : '{{URL::action("ApiScopeGroupController@create",null)}}',
fetchUsers: '{{URL::action("ApiScopeGroupController@fetchUsers",null)}}'
fetchUsers: '{{URL::action("UserApiController@fetch",null)}}'
};
var all_scopes = [];
@ -77,7 +77,7 @@
@endforeach
@foreach($group->users()->get() as $user)
current_users.push({ "id": {{$user->id}} , "value": "{{$user->getFullName().' ( '.$user->email.' )' }}" });
current_users.push({ "id": {{$user->id}} , "value": "{{$user->getFullName() }}" });
@endforeach
</script>

View File

@ -15,25 +15,31 @@
<table id='tclients' class="table table-hover table-condensed">
<thead>
<tr>
<th>&nbsp;</th>
<th>Application Name</th>
<th>Application Type</th>
<th>Is Active</th>
<th>Is Locked</th>
<th>Modified</th>
<th>Modified By</th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody id="body-registered-clients">
@foreach ($clients as $client)
<tr>
<td>@if (!$client->isOwner(Auth::user()))<i title="you have admin rights on this application" class="fa fa-user"></i>@endif</td>
<td>{{ $client->app_name }}</td>
<td>{{ $client->getFriendlyApplicationType()}}</td>
<td>
@if ($client->isOwner(Auth::user()))
<input type="checkbox" class="app-active-checkbox" id="app-active_{{$client->id}}"
@if ( $client->active)
@if ( $client->active)
checked
@endif
value="{{$client->id}}"/>
@endif
</td>
<td>
<input type="checkbox" class="app-locked-checkbox" id="app-locked_{{$client->id}}"
@ -43,9 +49,12 @@
value="{{$client->id}}" disabled="disabled" />
</td>
<td>{{ $client->updated_at }}</td>
<td>{{ $client->getEditedByNice() }}</td>
<td>&nbsp;
{{ HTML::link(URL::action("AdminController@editRegisteredClient",array("id"=>$client->id)),'Edit',array('class'=>'btn btn-default btn-md active edit-client','title'=>'Edits a Registered Application')) }}
@if ($client->canDelete(Auth::user()))
{{ HTML::link(URL::action("ClientApiController@delete",array("id"=>$client->id)),'Delete',array('class'=>'btn btn-default btn-md active del-client','title'=>'Deletes a Registered Application')) }}</td>
@endif
</tr>
@endforeach
</tbody>
@ -87,8 +96,16 @@
delete: '{{ URL::action("ClientApiController@delete",array("id"=>"@id")) }}',
add: '{{URL::action("ClientApiController@create",null)}}',
activate: '{{ URL::action("ClientApiController@activate",array("id"=>"@id")) }}',
deactivate: '{{ URL::action("ClientApiController@deactivate",array("id"=>"@id")) }}'
deactivate: '{{ URL::action("ClientApiController@deactivate",array("id"=>"@id")) }}',
fetchUsers: '{{URL::action("UserApiController@fetch",null)}}',
};
</script>
{{ HTML::script('bower_assets/typeahead.js/dist/typeahead.bundle.js')}}
{{ HTML::script('bower_assets/bootstrap-tagsinput/dist/bootstrap-tagsinput.js')}}
{{ HTML::script('assets/js/oauth2/profile/clients.js') }}
@stop
@stop
@section('css')
{{ HTML::style('bower_assets/bootstrap-tagsinput/dist/bootstrap-tagsinput.css') }}
{{ HTML::style('bower_assets/bootstrap-tagsinput/dist/bootstrap-tagsinput-typeahead.css') }}
@append

View File

@ -25,7 +25,9 @@
<div class="col-md-12">
<label for="client_secret" class="label-client-secret">Client Secret</label>
<span id="client_secret">{{ $client->client_secret }}</span>
@if ($client->isOwner(Auth::user()))
{{ HTML::link(URL::action("ClientApiController@regenerateClientSecret",array("id"=>$client->id)),'Regenerate',array('class'=>'btn btn-default btn-md active regenerate-client-secret','title'=>'Regenerates Client Secret')) }}
@endif
</div>
</div>
@endif
@ -74,6 +76,11 @@
<div class="col-md-12">
<form id="form-application-main-data" name="form-application-main-data">
<div class="form-group">
<label class="control-label" for="admin_users">Admin Users&nbsp;<span class="glyphicon glyphicon-info-sign accordion-toggle" aria-hidden="true" title="Choose which users would be administrator of this application"></span></label>
<input type="text" class="form-control" name="admin_users" id="admin_users" @if (!$client->isOwner(Auth::user()))disabled @endif>
</div>
<div class="form-group">
<label for="website">Application Web Site Url (optional)&nbsp;<span class="glyphicon glyphicon-info-sign accordion-toggle" aria-hidden="true"
title="URL of the home page of the Client"></span></label>

View File

@ -6,8 +6,11 @@
@section('css')
{{ HTML::style('bower_assets/bootstrap-tagsinput/dist/bootstrap-tagsinput.css') }}
{{ HTML::style('bower_assets/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css') }}
{{ HTML::style('bower_assets/bootstrap-tagsinput/dist/bootstrap-tagsinput-typeahead.css') }}
{{ HTML::style('assets/css/edit-client.css') }}
@append
@section('scripts')
{{ HTML::script('bower_assets/typeahead.js/dist/typeahead.bundle.js')}}
{{ HTML::script('bower_assets/bootstrap-tagsinput/dist/bootstrap-tagsinput.js')}}
{{ HTML::script('bower_assets/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js')}}
<script type="application/javascript">
@ -20,7 +23,8 @@
add_public_key: '{{URL::action("ClientPublicKeyApiController@create",array("id"=>$client->id))}}',
get_public_keys: '{{URL::action("ClientPublicKeyApiController@getByPage",array("id"=>$client->id))}}',
delete_public_key: '{{URL::action("ClientPublicKeyApiController@delete",array("id" => $client->id, 'public_key_id'=> '@public_key_id'))}}',
update_public_key: '{{URL::action("ClientPublicKeyApiController@update",array("id" => $client->id, 'public_key_id'=> '@public_key_id'))}}'
update_public_key: '{{URL::action("ClientPublicKeyApiController@update",array("id" => $client->id, 'public_key_id'=> '@public_key_id'))}}',
fetchUsers: '{{URL::action("UserApiController@fetch",null)}}',
};
var oauth2_supported_algorithms =
@ -33,6 +37,11 @@
key_management_algorihtms: {{utils\ArrayUtils::toJson(oauth2\OAuth2Protocol::$supported_key_management_algorithms)}},
content_encryption_algorihtms: {{utils\ArrayUtils::toJson(oauth2\OAuth2Protocol::$supported_content_encryption_algorithms)}}
};
var current_admin_users = [];
@foreach($client->admin_users()->get() as $user)
current_admin_users.push({ "id": {{$user->id}} , "value": "{{ $user->getFullName() }}" });
@endforeach
$(document).ready
(
@ -44,7 +53,18 @@
@append
@section('content')
@include('menu',array('is_oauth2_admin' => $is_oauth2_admin, 'is_openstackid_admin' => $is_openstackid_admin))
<legend><span aria-hidden="true" class="glyphicon glyphicon-info-sign pointable" title="OAuth 2.0 allows users to share specific data with you (for example, contact lists) while keeping their usernames, passwords, and other information private."></span>&nbsp;{{$client->getFriendlyApplicationType()}} - Client {{ $client->app_name }}</legend>
<legend>
<span aria-hidden="true" class="glyphicon glyphicon-info-sign pointable"
title="OAuth 2.0 allows users to share specific data with you (for example, contact lists) while keeping their usernames, passwords, and other information private.">
</span>&nbsp;{{$client->getFriendlyApplicationType()}} - Client {{ $client->app_name }}
</legend>
<div class="row">
<div style="padding-left:15px" class="col-md-2 clear-padding"><strong>Created By:&nbsp;</strong></div><div class="col-md-10 clear-padding">{{ $client->getOwnerNice() }}</div>
</div>
<div class="row">
<div style="padding-left:15px" class="col-md-2 clear-padding"><strong>Edited By</strong>:&nbsp;</div><div class="col-md-10 clear-padding">{{ $client->getEditedByNice() }}</div>
</div>
@if($errors->any())
<div class="errors">
<ul>

View File

@ -35,10 +35,11 @@
"bootstrap-tagsinput": "0.7.1",
"bootstrap-datepicker": "~1.4.0",
"pure-templates": "*",
"pwstrength-bootstrap":"~1.2.7",
"uri.js":"~1.15.2",
"crypto-js":"~3.1.5",
"pwstrength-bootstrap": "~1.2.7",
"uri.js": "~1.15.2",
"crypto-js": "~3.1.5",
"jquery-cookie": "~1.4.1",
"typeahead.js": "0.11.1"
"typeahead.js": "0.11.1",
"sweetalert": "~1.1.3"
}
}

View File

@ -42,7 +42,8 @@
"app/strategies",
"app/filters",
"app/validators",
"app/providers"
"app/providers",
"app/factories"
]
},
"scripts": {

View File

View File

@ -183,4 +183,19 @@ textarea {
.col-md-offset-neg-1 {
margin-left: -15%;
}
.clear-padding
{
padding:0;
}
.formatted-credential
{
text-align: left;
}
.auto-width
{
width: auto !important;
}

View File

@ -9,13 +9,17 @@ function loadClients(){
success: function (data,textStatus,jqXHR) {
//load data...
var clients = data.page;
var template = $('<tbody><tr><td class="app-name"></td><td class="client-type"></td><td class="client-active"><input type="checkbox" class="app-active-checkbox"></td><td class="client-locked"><input type="checkbox" disabled="disabled" class="app-locked-checkbox"></td><td class="client-modified"></td><td class="client-actions">&nbsp;<a class="btn btn-default btn-md active edit-client" title="Edits a Registered Application">Edit</a>&nbsp;<a class="btn btn-default btn-md active del-client" title="Deletes a Registered Application">Delete</a></td></tr></tbody>');
var template = $('<tbody><tr><td class="admin-app"></td><td class="app-name"></td><td class="client-type"></td><td class="client-active"><input type="checkbox" class="app-active-checkbox"></td><td class="client-locked"><input type="checkbox" disabled="disabled" class="app-locked-checkbox"></td><td class="client-modified"></td><td class="client-modified-by"></td><td class="client-actions">&nbsp;<a class="btn btn-default btn-md active edit-client" title="Edits a Registered Application">Edit</a>&nbsp;<a class="btn btn-default btn-md active del-client" title="Deletes a Registered Application">Delete</a></td></tr></tbody>');
var directives = {
'tr':{
'client<-context':{
'td.admin-app':function(arg){
return arg.item.is_own?'':'<i title="you have admin rights on this application" class="fa fa-user"></i>';
},
'td.app-name':'client.app_name',
'td.client-type':'client.application_type',
'td.client-modified':'client.updated_at',
'td.client-modified-by':'client.modified_by',
'.app-active-checkbox@value':'client.id',
'.app-active-checkbox@checked':function(arg){
return arg.item.active?'true':'';
@ -41,12 +45,19 @@ function loadClients(){
var client_id = arg.item.id;
var href = clientsUrls.delete;
return href.replace('@id',client_id);
},
'a.del-client@class+':function(arg)
{
return arg.item.is_own?'':' hidden';
},
'.app-active-checkbox@class+':function(arg){
return arg.item.is_own?'':' hidden';
}
}
}
};
var body = template.render(clients, directives);
var table = $('<table id="tclients" class="table table-hover table-condensed"><thead><tr><th>Application Name</th><th>Type</th><th>Is Active</th><th>Is Locked</th><th>Modified</th><th>&nbsp;</th></tr></thead>'+body.html()+'</table>');
var table = $('<table id="tclients" class="table table-hover table-condensed"><thead><tr><th>&nbsp;</th><th>Application Name</th><th>Type</th><th>Is Active</th><th>Is Locked</th><th>Modified</th><th>Modified By</th><th>&nbsp;</th></tr></thead>'+body.html()+'</table>');
$('#tclients','#clients').remove();
$('#clients').append(table);
@ -72,6 +83,35 @@ jQuery(document).ready(function($){
}
});
var users = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: clientsUrls.fetchUsers+'?t=%QUERY',
wildcard: '%QUERY'
}
});
$('#admin_users').tagsinput({
itemValue: 'id',
itemText: 'value',
freeInput: false,
allowDuplicates: false,
trimValue: true,
typeaheadjs: [
{
hint: true,
highlight: true,
minLength: 1
},
{
name: 'users',
displayKey: 'value',
source: users
}
]
});
application_dialog.modal({
show:false,
backdrop:"static"
@ -104,6 +144,17 @@ jQuery(document).ready(function($){
success: function (data,textStatus,jqXHR) {
loadClients();
application_dialog.modal('hide');
var client_id = data.client_id;
var client_secret = data.client_secret;
swal({
title: "Your Client Credentials!",
text: "<p><strong>CLIENT ID</strong></p><p class='formatted-credential'>"+client_id+"</p><p><strong>CLIENT SECRET</strong></p><p class='formatted-credential'>"+client_secret+"</p>",
html: true,
type: "info",
customClass: "auto-width"
});
},
error: function (jqXHR, textStatus, errorThrown) {
ajaxError(jqXHR, textStatus, errorThrown);
@ -115,24 +166,33 @@ jQuery(document).ready(function($){
});
$("body").on('click',".del-client",function(event){
if(confirm("Are you sure to delete this registered application?")){
var url = $(this).attr('href');
$.ajax(
{
type: "DELETE",
url: url,
contentType: "application/json; charset=utf-8",
dataType: "json",
timeout:60000,
success: function (data,textStatus,jqXHR) {
loadClients();
},
error: function (jqXHR, textStatus, errorThrown) {
ajaxError(jqXHR, textStatus, errorThrown);
var url = $(this).attr('href');
swal({
title: "Are you sure to delete this registered application?",
text: "This is an non reversible process!",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "Yes, Delete it!",
closeOnConfirm: true
},
function(){
$.ajax(
{
type: "DELETE",
url: url,
contentType: "application/json; charset=utf-8",
dataType: "json",
timeout:60000,
success: function (data,textStatus,jqXHR) {
loadClients();
},
error: function (jqXHR, textStatus, errorThrown) {
ajaxError(jqXHR, textStatus, errorThrown);
}
}
}
);
}
);
});
event.preventDefault();
return false;
});

View File

@ -17,6 +17,40 @@ jQuery(document).ready(function($){
event.cancel = true;
});
var users = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: dataClientUrls.fetchUsers+'?t=%QUERY',
wildcard: '%QUERY'
}
});
$('#admin_users').tagsinput({
itemValue: 'id',
itemText: 'value',
freeInput: false,
allowDuplicates: false,
trimValue: true,
typeaheadjs: [
{
hint: true,
highlight: true,
minLength: 1
},
{
name: 'users',
displayKey: 'value',
source: users
}
]
});
for(var user of current_admin_users)
{
$('#admin_users').tagsinput('add',user);
}
$('#redirect_uris').tagsinput({
trimValue: true,
onTagExists: function(item, $tag) {
@ -51,8 +85,17 @@ jQuery(document).ready(function($){
});
$("body").on('click',".regenerate-client-secret",function(event){
if(confirm("Are you sure? Regenerating client secret would invalidate all current tokens")){
var link = $(this).attr('href');
var link = $(this).attr('href');
swal({
title: "Are you sure?",
text: "Regenerating client secret would invalidate all current tokens!",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "Yes, Regenerate it!",
closeOnConfirm: true
},
function(){
$.ajax(
{
type: "PUT",
@ -72,7 +115,7 @@ jQuery(document).ready(function($){
}
}
);
}
});
event.preventDefault();
return false;
});

View File

@ -77,33 +77,39 @@
});
$("body").on('click',".delete-public-key",function(event){
if(window.confirm('are you sure?')){
//delete key
var public_key_id = $(this).attr('data-public-key-id');
$.ajax(
{
type: "DELETE",
url: dataClientUrls.delete_public_key.replace('@public_key_id', public_key_id),
contentType: "application/json; charset=utf-8",
dataType: "json",
timeout: 60000,
success: function (data, textStatus, jqXHR) {
$('#tr_'+public_key_id).fadeOut(300, function() {
$(this).remove();
if($('#body-public-keys').children('tr').length)
$('.public-keys-empty-message').hide();
else
$('.public-keys-empty-message').show();
});
},
error: function (jqXHR, textStatus, errorThrown) {
ajaxError(jqXHR, textStatus, errorThrown);
var public_key_id = $(this).attr('data-public-key-id');
swal({
title: "Are you sure?",
text: "Deleting this public key is a non reversible operation!",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "Yes, Delete it!",
closeOnConfirm: true
},
function(){
$.ajax(
{
type: "DELETE",
url: dataClientUrls.delete_public_key.replace('@public_key_id', public_key_id),
contentType: "application/json; charset=utf-8",
dataType: "json",
timeout: 60000,
success: function (data, textStatus, jqXHR) {
$('#tr_'+public_key_id).fadeOut(300, function() {
$(this).remove();
if($('#body-public-keys').children('tr').length)
$('.public-keys-empty-message').hide();
else
$('.public-keys-empty-message').show();
});
},
error: function (jqXHR, textStatus, errorThrown) {
ajaxError(jqXHR, textStatus, errorThrown);
}
}
}
);
}
);
});
event.preventDefault();
return false;
});