PK@ Zbouncer/composer.jsonnu[{ "name": "silber/bouncer", "description": "Eloquent roles and abilities.", "keywords": [ "abilities", "acl", "capabilities", "eloquent", "laravel", "permissions", "roles" ], "license": "MIT", "authors": [ { "name": "Joseph Silber", "email": "contact@josephsilber.com" } ], "autoload": { "psr-4": { "Silber\\Bouncer\\": "src/" } }, "autoload-dev": { "psr-4": { "Silber\\Bouncer\\Tests\\": "tests/" }, "files": [ "tests/helpers.php" ] }, "require": { "php": "^7.2|^8.0", "illuminate/auth": "^6.0|^7.0|^8.0|^9.0|^10.0", "illuminate/cache": "^6.0|^7.0|^8.0|^9.0|^10.0", "illuminate/container": "^6.0|^7.0|^8.0|^9.0|^10.0", "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0", "illuminate/database": "^6.0|^7.0|^8.0|^9.0|^10.0" }, "require-dev": { "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0", "illuminate/events": "^6.0|^7.0|^8.0|^9.0|^10.0", "larapack/dd": "^1.1", "mockery/mockery": "^1.3.3", "phpunit/phpunit": "^8.0|^9.0" }, "suggest": { "illuminate/console": "Allows running the bouncer:clean artisan command", "illuminate/events": "Required for multi-tenancy support" }, "scripts": { "test": "phpunit" }, "minimum-stability": "dev", "prefer-stable": true, "extra": { "laravel": { "providers": [ "Silber\\Bouncer\\BouncerServiceProvider" ], "aliases": { "Bouncer": "Silber\\Bouncer\\BouncerFacade" } } } } PK@ ZKP&P&bouncer/src/CachedClipboard.phpnu[setCache($cache); } /** * Set the cache instance. * * @param \Illuminate\Contracts\Cache\Store $cache * @return $this */ public function setCache(Store $cache) { if (method_exists($cache, 'tags')) { $cache = $cache->tags($this->tag()); } $this->cache = $cache; return $this; } /** * Get the cache instance. * * @return \Illuminate\Contracts\Cache\Store */ public function getCache() { return $this->cache; } /** * Determine if the given authority has the given ability, and return the ability ID. * * @param \Illuminate\Database\Eloquent\Model $authority * @param string $ability * @param \Illuminate\Database\Eloquent\Model|string|null $model * @return int|bool|null */ public function checkGetId(Model $authority, $ability, $model = null) { $applicable = $this->compileAbilityIdentifiers($ability, $model); // We will first check if any of the applicable abilities have been forbidden. // If so, we'll return false right away, so as to not pass the check. Then, // we'll check if any of them have been allowed & return the matched ID. $forbiddenId = $this->findMatchingAbility( $this->getForbiddenAbilities($authority), $applicable, $model, $authority ); if ($forbiddenId) { return false; } return $this->findMatchingAbility( $this->getAbilities($authority), $applicable, $model, $authority ); } /** * Determine if any of the abilities can be matched against the provided applicable ones. * * @param \Illuminate\Support\Collection $abilities * @param \Illuminate\Support\Collection $applicable * @param \Illuminate\Database\Eloquent\Model $model * @param \Illuminate\Database\Eloquent\Model $authority * @return int|null */ protected function findMatchingAbility($abilities, $applicable, $model, $authority) { $abilities = $abilities->toBase()->pluck('identifier', 'id'); if ($id = $this->getMatchedAbilityId($abilities, $applicable)) { return $id; } if ($this->isOwnedBy($authority, $model)) { return $this->getMatchedAbilityId( $abilities, $applicable->map(function ($identifier) { return $identifier.'-owned'; }) ); } } /** * Get the ID of the ability that matches one of the applicable abilities. * * @param \Illuminate\Support\Collection $abilityMap * @param \Illuminate\Support\Collection $applicable * @return int|null */ protected function getMatchedAbilityId($abilityMap, $applicable) { foreach ($abilityMap as $id => $identifier) { if ($applicable->contains($identifier)) { return $id; } } } /** * Compile a list of ability identifiers that match the provided parameters. * * @param string $ability * @param \Illuminate\Database\Eloquent\Model|string|null $model * @return \Illuminate\Support\Collection */ protected function compileAbilityIdentifiers($ability, $model) { $identifiers = new BaseCollection( is_null($model) ? [$ability, '*-*', '*'] : $this->compileModelAbilityIdentifiers($ability, $model) ); return $identifiers->map(function ($identifier) { return strtolower($identifier); }); } /** * Compile a list of ability identifiers that match the given model. * * @param string $ability * @param \Illuminate\Database\Eloquent\Model|string $model * @return array */ protected function compileModelAbilityIdentifiers($ability, $model) { if ($model === '*') { return ["{$ability}-*", "*-*"]; } $model = $model instanceof Model ? $model : new $model; $type = $model->getMorphClass(); $abilities = [ "{$ability}-{$type}", "{$ability}-*", "*-{$type}", "*-*", ]; if ($model->exists) { $abilities[] = "{$ability}-{$type}-{$model->getKey()}"; $abilities[] = "*-{$type}-{$model->getKey()}"; } return $abilities; } /** * Get the given authority's abilities. * * @param \Illuminate\Database\Eloquent\Model $authority * @param bool $allowed * @return \Illuminate\Database\Eloquent\Collection */ public function getAbilities(Model $authority, $allowed = true) { $key = $this->getCacheKey($authority, 'abilities', $allowed); if (is_array($abilities = $this->cache->get($key))) { return $this->deserializeAbilities($abilities); } $abilities = $this->getFreshAbilities($authority, $allowed); $this->cache->forever($key, $this->serializeAbilities($abilities)); return $abilities; } /** * Get a fresh copy of the given authority's abilities. * * @param \Illuminate\Database\Eloquent\Model $authority * @param bool $allowed * @return \Illuminate\Database\Eloquent\Collection */ public function getFreshAbilities(Model $authority, $allowed) { return parent::getAbilities($authority, $allowed); } /** * Get the given authority's roles' IDs and names. * * @param \Illuminate\Database\Eloquent\Model $authority * @return array */ public function getRolesLookup(Model $authority) { $key = $this->getCacheKey($authority, 'roles'); return $this->sear($key, function () use ($authority) { return parent::getRolesLookup($authority); }); } /** * Get an item from the cache, or store the default value forever. * * @param string $key * @param callable $callback * @return mixed */ protected function sear($key, callable $callback) { if (is_null($value = $this->cache->get($key))) { $this->cache->forever($key, $value = $callback()); } return $value; } /** * Clear the cache. * * @param null|\Illuminate\Database\Eloquent\Model $authority * @return $this */ public function refresh($authority = null) { if ( ! is_null($authority)) { return $this->refreshFor($authority); } if ($this->cache instanceof TaggedCache) { $this->cache->flush(); } else { $this->refreshAllIteratively(); } return $this; } /** * Clear the cache for the given authority. * * @param \Illuminate\Database\Eloquent\Model $authority * @return $this */ public function refreshFor(Model $authority) { $this->cache->forget($this->getCacheKey($authority, 'abilities', true)); $this->cache->forget($this->getCacheKey($authority, 'abilities', false)); $this->cache->forget($this->getCacheKey($authority, 'roles')); return $this; } /** * Refresh the cache for all roles and users, iteratively. * * @return void */ protected function refreshAllIteratively() { foreach (Models::user()->all() as $user) { $this->refreshFor($user); } foreach (Models::role()->all() as $role) { $this->refreshFor($role); } } /** * Get the cache key for the given model's cache type. * * @param \Illuminate\Database\Eloquent\Model $model * @param string $type * @param bool $allowed * @return string */ protected function getCacheKey(Model $model, $type, $allowed = true) { return implode('-', [ $this->tag(), $type, $model->getMorphClass(), $model->getKey(), $allowed ? 'a' : 'f', ]); } /** * Get the cache tag. * * @return string */ protected function tag() { return Models::scope()->appendToCacheKey($this->tag); } /** * Deserialize an array of abilities into a collection of models. * * @param array $abilities * @return \Illuminate\Database\Eloquent\Collection */ protected function deserializeAbilities(array $abilities) { return Models::ability()->hydrate($abilities); } /** * Serialize a collection of ability models into a plain array. * * @param \Illuminate\Database\Eloquent\Collection $abilities * @return array */ protected function serializeAbilities(Collection $abilities) { return $abilities->map(function ($ability) { return $ability->getAttributes(); })->all(); } } PK@ Z5%bouncer/src/Database/Titles/Title.phpnu[title; } } PK@ Zʆ)bouncer/src/Database/Titles/RoleTitle.phpnu[title = $this->humanize($role->name); } } PK@ Znmm,bouncer/src/Database/Titles/AbilityTitle.phpnu[isWildcardAbility($ability)) { $this->title = $this->getWildcardAbilityTitle($ability); } else if ($this->isRestrictedWildcardAbility($ability)) { $this->title = 'All simple abilities'; } else if ($this->isSimpleAbility($ability)) { $this->title = $this->humanize($ability->name); } else if ($this->isRestrictedOwnershipAbility($ability)) { $this->title = $this->humanize($ability->name.' everything owned'); } else if ($this->isGeneralManagementAbility($ability)) { $this->title = $this->getBlanketModelAbilityTitle($ability); } else if ($this->isBlanketModelAbility($ability)) { $this->title = $this->getBlanketModelAbilityTitle($ability, $ability->name); } else if ($this->isSpecificModelAbility($ability)) { $this->title = $this->getSpecificModelAbilityTitle($ability); } else if ($this->isGlobalActionAbility($ability)) { $this->title = $this->humanize($ability->name.' everything'); } } /** * Determines if the given ability allows all abilities. * * @param \Illuminate\Database\Eloquent\model $ability * @return bool */ protected function isWildcardAbility(Model $ability) { return $ability->name === '*' && $ability->entity_type === '*'; } /** * Determines if the given ability allows all simple abilities. * * @param \Illuminate\Database\Eloquent\model $ability * @return bool */ protected function isRestrictedWildcardAbility(Model $ability) { return $ability->name === '*' && is_null($ability->entity_type); } /** * Determines if the given ability is a simple (non model) ability. * * @param \Illuminate\Database\Eloquent\model $ability * @return bool */ protected function isSimpleAbility(Model $ability) { return is_null($ability->entity_type); } /** * Determines whether the given ability is a global * ownership ability restricted to a specific action. * * @param \Illuminate\Database\Eloquent\model $ability * @return bool */ protected function isRestrictedOwnershipAbility(Model $ability) { return $ability->only_owned && $ability->name !== '*' && $ability->entity_type === '*'; } /** * Determines whether the given ability is for managing all models of a given type. * * @param \Illuminate\Database\Eloquent\model $ability * @return bool */ protected function isGeneralManagementAbility(Model $ability) { return $ability->name === '*' && $ability->entity_type !== '*' && ! is_null($ability->entity_type) && is_null($ability->entity_id); } /** * Determines whether the given ability is for an action on all models of a given type. * * @param \Illuminate\Database\Eloquent\model $ability * @return bool */ protected function isBlanketModelAbility(Model $ability) { return $ability->name !== '*' && $ability->entity_type !== '*' && ! is_null($ability->entity_type) && is_null($ability->entity_id); } /** * Determines whether the given ability is for an action on a specific model. * * @param \Illuminate\Database\Eloquent\model $ability * @return bool */ protected function isSpecificModelAbility(Model $ability) { return $ability->entity_type !== '*' && ! is_null($ability->entity_type) && ! is_null($ability->entity_id); } /** * Determines whether the given ability allows an action on all models. * * @param \Illuminate\Database\Eloquent\model $ability * @return bool */ protected function isGlobalActionAbility(Model $ability) { return $ability->name !== '*' && $ability->entity_type === '*' && is_null($ability->entity_id); } /** * Get the title for the given wildcard ability. * * @param \Illuminate\Database\Eloquent\model $ability * @return string */ protected function getWildcardAbilityTitle(Model $ability) { if ($ability->only_owned) { return 'Manage everything owned'; } return 'All abilities'; } /** * Get the title for the given blanket model ability. * * @param \Illuminate\Database\Eloquent\model $ability * @param string $name * @return string */ protected function getBlanketModelAbilityTitle(Model $ability, $name = 'manage') { return $this->humanize($name.' '.$this->getPluralName($ability->entity_type)); } /** * Get the title for the given model ability. * * @param \Illuminate\Database\Eloquent\model $ability * @return string */ protected function getSpecificModelAbilityTitle(Model $ability) { $name = $ability->name === '*' ? 'manage' : $ability->name; return $this->humanize( $name.' '.$this->basename($ability->entity_type).' #'.$ability->entity_id ); } /** * Get the human-readable plural form of the given class name. * * @param string $class * @return string */ protected function getPluralName($class) { return $this->pluralize($this->basename($class)); } /** * Get the class "basename" of the given class. * * @param string $class * @return string */ protected function basename($class) { return basename(str_replace('\\', '/', $class)); } /** * Pluralize the given value. * * @param string $value * @return string */ protected function pluralize($value) { return Str::plural($value, 2); } } PK@ Z VM M &bouncer/src/Database/Queries/Roles.phpnu[whereHas('roles', function ($query) use ($roles) { $query->whereIn('name', $roles); }); } /** * Constrain the given query by all provided roles. * * @param \Illuminate\Database\Eloquent\Builder $query * @param string ...$roles * @return \Illuminate\Database\Eloquent\Builder */ public function constrainWhereIsAll($query, ...$roles) { return $query->whereHas('roles', function ($query) use ($roles) { $query->whereIn('name', $roles); }, '=', count($roles)); } /** * Constrain the given query by the provided role. * * @param \Illuminate\Database\Eloquent\Builder $query * @param string ...$roles * @return \Illuminate\Database\Eloquent\Builder */ public function constrainWhereIsNot($query, ...$roles) { return $query->whereDoesntHave('roles', function ($query) use ($roles) { $query->whereIn('name', $roles); }); } /** * Constrain the given roles query to those that were assigned to the given authorities. * * @param \Illuminate\Database\Eloquent\Builder $query * @param string|\Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection $model * @param array $keys * @return void */ public function constrainWhereAssignedTo($query, $model, array $keys = null) { list($model, $keys) = Helpers::extractModelAndKeys($model, $keys); $query->whereExists(function ($query) use ($model, $keys) { $table = $model->getTable(); $key = "{$table}.{$model->getKeyName()}"; $pivot = Models::table('assigned_roles'); $roles = Models::table('roles'); $query->from($table) ->join($pivot, $key, '=', $pivot.'.entity_id') ->whereColumn("{$pivot}.role_id", "{$roles}.id") ->where("{$pivot}.entity_type", $model->getMorphClass()) ->whereIn($key, $keys); Models::scope()->applyToModelQuery($query, $roles); Models::scope()->applyToRelationQuery($query, $pivot); }); } } PK@ Z]ي*bouncer/src/Database/Queries/Abilities.phpnu[where(function ($query) use ($authority, $allowed) { $query->whereExists(static::getRoleConstraint($authority, $allowed)); $query->orWhereExists(static::getAuthorityConstraint($authority, $allowed)); $query->orWhereExists(static::getEveryoneConstraint($allowed)); }); } /** * Get a query for the authority's forbidden abilities. * * @param \Illuminate\Database\Eloquent\Model $authority * @return \Illuminate\Database\Eloquent\Builder */ public static function forbiddenForAuthority(Model $authority) { return static::forAuthority($authority, false); } /** * Get a constraint for abilities that have been granted to the given authority through a role. * * @param \Illuminate\Database\Eloquent\Model $authority * @param bool $allowed * @return \Closure */ protected static function getRoleConstraint(Model $authority, $allowed) { return function ($query) use ($authority, $allowed) { $permissions = Models::table('permissions'); $abilities = Models::table('abilities'); $roles = Models::table('roles'); $query->from($roles) ->join($permissions, $roles.'.id', '=', $permissions.'.entity_id') ->whereColumn("{$permissions}.ability_id", "{$abilities}.id") ->where($permissions.".forbidden", ! $allowed) ->where($permissions.".entity_type", Models::role()->getMorphClass()); Models::scope()->applyToModelQuery($query, $roles); Models::scope()->applyToRelationQuery($query, $permissions); $query->where(function ($query) use ($roles, $authority, $allowed) { $query->whereExists(static::getAuthorityRoleConstraint($authority)); }); }; } /** * Get a constraint for roles that are assigned to the given authority. * * @param \Illuminate\Database\Eloquent\Model $authority * @return \Closure */ protected static function getAuthorityRoleConstraint(Model $authority) { return function ($query) use ($authority) { $pivot = Models::table('assigned_roles'); $roles = Models::table('roles'); $table = $authority->getTable(); $query->from($table) ->join($pivot, "{$table}.{$authority->getKeyName()}", '=', $pivot.'.entity_id') ->whereColumn("{$pivot}.role_id", "{$roles}.id") ->where($pivot.'.entity_type', $authority->getMorphClass()) ->where("{$table}.{$authority->getKeyName()}", $authority->getKey()); Models::scope()->applyToModelQuery($query, $roles); Models::scope()->applyToRelationQuery($query, $pivot); }; } /** * Get a constraint for abilities that have been granted to the given authority. * * @param \Illuminate\Database\Eloquent\Model $authority * @param bool $allowed * @return \Closure */ protected static function getAuthorityConstraint(Model $authority, $allowed) { return function ($query) use ($authority, $allowed) { $permissions = Models::table('permissions'); $abilities = Models::table('abilities'); $table = $authority->getTable(); $query->from($table) ->join($permissions, "{$table}.{$authority->getKeyName()}", '=', $permissions.'.entity_id') ->whereColumn("{$permissions}.ability_id", "{$abilities}.id") ->where("{$permissions}.forbidden", ! $allowed) ->where("{$permissions}.entity_type", $authority->getMorphClass()) ->where("{$table}.{$authority->getKeyName()}", $authority->getKey()); Models::scope()->applyToModelQuery($query, $abilities); Models::scope()->applyToRelationQuery($query, $permissions); }; } /** * Get a constraint for abilities that have been granted to everyone. * * @param bool $allowed * @return \Closure */ protected static function getEveryoneConstraint($allowed) { return function ($query) use ($allowed) { $permissions = Models::table('permissions'); $abilities = Models::table('abilities'); $query->from($permissions) ->whereColumn("{$permissions}.ability_id", "{$abilities}.id") ->where("{$permissions}.forbidden", ! $allowed) ->whereNull('entity_id'); Models::scope()->applyToRelationQuery($query, $permissions); }; } } PK@ Z{ { 2bouncer/src/Database/Queries/AbilitiesForModel.phpnu[table = Models::table('abilities'); } /** * Constrain a query to an ability for a specific model or wildcard. * * @param \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder $query * @param \Illuminate\Database\Eloquent\Model|string $model * @param bool $strict * @return void */ public function constrain($query, $model, $strict = false) { if ($model === '*') { return $this->constrainByWildcard($query); } $model = is_string($model) ? new $model : $model; $this->constrainByModel($query, $model, $strict); } /** * Constrain a query to a model wiildcard. * * @param \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder $query * @return void */ protected function constrainByWildcard($query) { $query->where("{$this->table}.entity_type", '*'); } /** * Constrain a query to an ability for a specific model. * * @param \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder $query * @param \Illuminate\Database\Eloquent\Model $model * @param bool $strict * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder */ protected function constrainByModel($query, Model $model, $strict) { if ($strict) { return $query->where( $this->modelAbilityConstraint($model, $strict) ); } return $query->where(function ($query) use ($model, $strict) { $query->where("{$this->table}.entity_type", '*') ->orWhere($this->modelAbilityConstraint($model, $strict)); }); } /** * Get the constraint for regular model abilities. * * @param \Illuminate\Database\Eloquent\Model $model * @param bool $strict * @return \Closure */ protected function modelAbilityConstraint(Model $model, $strict) { return function ($query) use ($model, $strict) { $query->where("{$this->table}.entity_type", $model->getMorphClass()); $query->where($this->abilitySubqueryConstraint($model, $strict)); }; } /** * Get the constraint for the ability subquery. * * @param \Illuminate\Database\Eloquent\Model $model * @param bool $strict * @return \Closure */ protected function abilitySubqueryConstraint(Model $model, $strict) { return function ($query) use ($model, $strict) { // If the model does not exist, we want to search for blanket abilities // that cover all instances of this model. If it does exist, we only // want to find blanket abilities if we're not using strict mode. if ( ! $model->exists || ! $strict) { $query->whereNull("{$this->table}.entity_id"); } if ($model->exists) { $query->orWhere("{$this->table}.entity_id", $model->getKey()); } }; } } PK@ ZI"m bouncer/src/Database/Ability.phpnu[ 'int', 'entity_id' => 'int', 'only_owned' => 'boolean', ]; /** * Constructor. * * @param array $attributes */ public function __construct(array $attributes = []) { $this->table = Models::table('abilities'); parent::__construct($attributes); } } PK@ Z(#bouncer/src/Database/Role.phpnu[ 'int', ]; /** * Constructor. * * @param array $attributes */ public function __construct(array $attributes = []) { $this->table = Models::table('roles'); parent::__construct($attributes); } } PK@ Z}ZZbouncer/src/Database/Models.phpnu[getTable(); } /** * Set custom table names. * * @param array $map * @return void */ public static function setTables(array $map) { static::$tables = array_merge(static::$tables, $map); static::updateMorphMap(); } /** * Get a custom table name mapping for the given table. * * @param string $table * @return string */ public static function table($table) { if (isset(static::$tables[$table])) { return static::$tables[$table]; } return $table; } /** * Get or set the model scoping instance. * * @param \Silber\Bouncer\Contracts\Scope|null $scope * @return mixed */ public static function scope(ScopeContract $scope = null) { if (! is_null($scope)) { return static::$scope = $scope; } if (is_null(static::$scope)) { static::$scope = new Scope; } return static::$scope; } /** * Get the classname mapping for the given model. * * @param string $model * @return string */ public static function classname($model) { if (isset(static::$models[$model])) { return static::$models[$model]; } return $model; } /** * Update Eloquent's morph map with the Bouncer models and tables. * * @param array|null $classNames * @return void */ public static function updateMorphMap($classNames = null) { if (is_null($classNames)) { $classNames = [ static::classname(Role::class), static::classname(Ability::class), ]; } Relation::morphMap($classNames); } /** * Register an attribute/callback to determine if a model is owned by a given authority. * * @param string|\Closure $model * @param string|\Closure|null $attribute * @return void */ public static function ownedVia($model, $attribute = null) { if (is_null($attribute)) { static::$ownership['*'] = $model; } static::$ownership[$model] = $attribute; } /** * Determines whether the given model is owned by the given authority. * * @param \Illuminate\Database\Eloquent\Model $authority * @param \Illuminate\Database\Eloquent\Model $model * @return bool */ public static function isOwnedBy(Model $authority, Model $model) { $type = get_class($model); if (isset(static::$ownership[$type])) { $attribute = static::$ownership[$type]; } elseif (isset(static::$ownership['*'])) { $attribute = static::$ownership['*']; } else { $attribute = strtolower(static::basename($authority)).'_id'; } return static::isOwnedVia($attribute, $authority, $model); } /** * Determines ownership via the given attribute. * * @param string|\Closure $attribute * @param \Illuminate\Database\Eloquent\Model $authority * @param \Illuminate\Database\Eloquent\Model $model * @return bool */ protected static function isOwnedVia($attribute, Model $authority, Model $model) { if ($attribute instanceof Closure) { return $attribute($model, $authority); } return $authority->getKey() == $model->{$attribute}; } /** * Get an instance of the ability model. * * @param array $attributes * @return \Silber\Bouncer\Database\Ability */ public static function ability(array $attributes = []) { return static::make(Ability::class, $attributes); } /** * Get an instance of the role model. * * @param array $attributes * @return \Silber\Bouncer\Database\Role */ public static function role(array $attributes = []) { return static::make(Role::class, $attributes); } /** * Get an instance of the user model. * * @param array $attributes * @return \Illuminate\Database\Eloquent\Model */ public static function user(array $attributes = []) { return static::make(User::class, $attributes); } /** * Get a new query builder instance. * * @param string $table * @return \Illuminate\Database\Query\Builder */ public static function query($table) { $query = new Builder( $connection = static::user()->getConnection(), $connection->getQueryGrammar(), $connection->getPostProcessor() ); return $query->from(static::table($table)); } /** * Reset all settings to their original state. * * @return void */ public static function reset() { static::$models = static::$tables = static::$ownership = []; } /** * Get an instance of the given model. * * @param string $model * @param array $attributes * @return \Illuminate\Database\Eloquent\Model */ protected static function make($model, array $attributes = []) { $model = static::classname($model); return new $model($attributes); } /** * Get the basename of the given class. * * @param string|object $class * @return string */ protected static function basename($class) { if ( ! is_string($class)) { $class = get_class($class); } $segments = explode('\\', $class); return end($segments); } } PK@ Z^?R!!*bouncer/src/Database/Concerns/HasRoles.phpnu[roles()->detach(); } }); } /** * The roles relationship. * * @return \Illuminate\Database\Eloquent\Relations\MorphToMany */ public function roles(): MorphToMany { $relation = $this->morphToMany( Models::classname(Role::class), 'entity', Models::table('assigned_roles') )->withPivot('scope'); return Models::scope()->applyToRelation($relation); } /** * Get all of the model's assigned roles. * * @return \Illuminate\Support\Collection */ public function getRoles() { return Container::getInstance() ->make(Clipboard::class) ->getRoles($this); } /** * Assign the given roles to the model. * * @param \Illuminate\Database\Eloquent\Model|string|array $roles * @return $this */ public function assign($roles) { (new AssignsRoles($roles))->to($this); return $this; } /** * Retract the given roles from the model. * * @param \Illuminate\Database\Eloquent\Model|string|array $roles * @return $this */ public function retract($roles) { (new RemovesRoles($roles))->from($this); return $this; } /** * Check if the model has any of the given roles. * * @param string ...$roles * @return bool */ public function isAn(...$roles) { return Container::getInstance() ->make(Clipboard::class) ->checkRole($this, $roles, 'or'); } /** * Check if the model has any of the given roles. * * Alias for the "isAn" method. * * @param string ...$roles * @return bool */ public function isA(...$roles) { return $this->isAn(...$roles); } /** * Check if the model has none of the given roles. * * @param string ...$roles * @return bool */ public function isNotAn(...$roles) { return Container::getInstance() ->make(Clipboard::class) ->checkRole($this, $roles, 'not'); } /** * Check if the model has none of the given roles. * * Alias for the "isNotAn" method. * * @param string ...$roles * @return bool */ public function isNotA(...$roles) { return $this->isNotAn(...$roles); } /** * Check if the model has all of the given roles. * * @param string ...$roles * @return bool */ public function isAll(...$roles) { return Container::getInstance() ->make(Clipboard::class) ->checkRole($this, $roles, 'and'); } /** * Constrain the given query by the provided role. * * @param \Illuminate\Database\Eloquent\Builder $query * @param string $role * @return void */ public function scopeWhereIs($query, $role) { (new RolesQuery)->constrainWhereIs(...func_get_args()); } /** * Constrain the given query by all provided roles. * * @param \Illuminate\Database\Eloquent\Builder $query * @param string $role * @return void */ public function scopeWhereIsAll($query, $role) { (new RolesQuery)->constrainWhereIsAll(...func_get_args()); } /** * Constrain the given query by the provided role. * * @param \Illuminate\Database\Eloquent\Builder $query * @param string $role * @return void */ public function scopeWhereIsNot($query, $role) { (new RolesQuery)->constrainWhereIsNot(...func_get_args()); } } PK@ ZH=Z(bouncer/src/Database/Concerns/IsRole.phpnu[applyToModel($role); if (is_null($role->title)) { $role->title = RoleTitle::from($role)->toString(); } }); static::deleted(function ($role) { $role->abilities()->detach(); }); } /** * The users relationship. * * @return \Illuminate\Database\Eloquent\Relations\MorphedToMany */ public function users(): MorphToMany { $relation = $this->morphedByMany( Models::classname(User::class), 'entity', Models::table('assigned_roles') )->withPivot('scope'); return Models::scope()->applyToRelation($relation); } /** * Assign the role to the given model(s). * * @param string|\Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection $model * @param array|null $keys * @return $this */ public function assignTo($model, array $keys = null) { list($model, $keys) = Helpers::extractModelAndKeys($model, $keys); $query = $this->newBaseQueryBuilder()->from(Models::table('assigned_roles')); $query->insert($this->createAssignRecords($model, $keys)); return $this; } /** * Find the given roles, creating the names that don't exist yet. * * @param iterable $roles * @return \Illuminate\Database\Eloquent\Collection */ public function findOrCreateRoles($roles) { $roles = Helpers::groupModelsAndIdentifiersByType($roles); $roles['integers'] = $this->find($roles['integers']); $roles['strings'] = $this->findOrCreateRolesByName($roles['strings']); return $this->newCollection(Arr::collapse($roles)); } /** * Find roles by name, creating the ones that don't exist. * * @param iterable $names * @return \Illuminate\Database\Eloquent\Collection */ protected function findOrCreateRolesByName($names) { if (empty($names)) { return []; } $existing = static::whereIn('name', $names)->get()->keyBy('name'); return (new Collection($names)) ->diff($existing->pluck('name')) ->map(function ($name) { return static::create(compact('name')); }) ->merge($existing); } /** * Get the IDs of the given roles. * * @param iterable $roles * @return array */ public function getRoleKeys($roles) { $roles = Helpers::groupModelsAndIdentifiersByType($roles); $roles['strings'] = $this->getKeysByName($roles['strings']); $roles['models'] = Arr::pluck($roles['models'], $this->getKeyName()); return Arr::collapse($roles); } /** * Get the names of the given roles. * * @param iterable $roles * @return array */ public function getRoleNames($roles) { $roles = Helpers::groupModelsAndIdentifiersByType($roles); $roles['integers'] = $this->getNamesByKey($roles['integers']); $roles['models'] = Arr::pluck($roles['models'], 'name'); return Arr::collapse($roles); } /** * Get the keys of the roles with the given names. * * @param iterable $names * @return array */ public function getKeysByName($names) { if (empty($names)) { return []; } return $this->whereIn('name', $names) ->select($this->getKeyName())->get() ->pluck($this->getKeyName())->all(); } /** * Get the names of the roles with the given IDs. * * @param iterable $keys * @return array */ public function getNamesByKey($keys) { if (empty($keys)) { return []; } return $this->whereIn($this->getKeyName(), $keys) ->select('name')->get() ->pluck('name')->all(); } /** * Retract the role from the given model(s). * * @param string|\Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection $model * @param array|null $keys * @return $this */ public function retractFrom($model, array $keys = null) { list($model, $keys) = Helpers::extractModelAndKeys($model, $keys); $query = $this->newBaseQueryBuilder() ->from(Models::table('assigned_roles')) ->where('role_id', $this->getKey()) ->where('entity_type', $model->getMorphClass()) ->whereIn('entity_id', $keys); Models::scope()->applyToRelationQuery($query, $query->from); $query->delete(); return $this; } /** * Create the pivot table records for assigning the role to given models. * * @param \Illuminate\Database\Eloquent\Model $model * @param array $keys * @return array */ protected function createAssignRecords(Model $model, array $keys) { $type = $model->getMorphClass(); return array_map(function ($key) use ($type) { return Models::scope()->getAttachAttributes() + [ 'role_id' => $this->getKey(), 'entity_type' => $type, 'entity_id' => $key, ]; }, $keys); } /** * Constrain the given query to roles that were assigned to the given authorities. * * @param \Illuminate\Database\Eloquent\Builder $query * @param string|\Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection $model * @param array $keys * @return void */ public function scopeWhereAssignedTo($query, $model, array $keys = null) { (new RolesQuery)->constrainWhereAssignedTo($query, $model, $keys); } } PK@ ZN+bouncer/src/Database/Concerns/IsAbility.phpnu[applyToModel($ability); if (is_null($ability->title)) { $ability->title = AbilityTitle::from($ability)->toString(); } }); } /** * Get the options attribute. * * @return array */ public function getOptionsAttribute() { if (empty($this->attributes['options'])) { return []; } return json_decode($this->attributes['options'], true); } /** * Set the "options" attribute. * * @param array $options * @return void */ public function setOptionsAttribute(array $options) { $this->attributes['options'] = json_encode($options); } /** * CHecks if the ability has constraints. * * @return bool */ public function hasConstraints() { return ! empty($this->options['constraints']); } /** * Get the ability's constraints. * * @return \Silber\Bouncer\Constraints\Constrainer */ public function getConstraints() { if (empty($this->options['constraints'])) { return new Group(); } $data = $this->options['constraints']; return $data['class']::fromData($data['params']); } /** * Set the ability's constraints. * * @param \Silber\Bouncer\Constraints\Constrainer $constrainer * @return $this */ public function setConstraints(Constrainer $constrainer) { $this->options = array_merge($this->options, [ 'constraints' => $constrainer->data(), ]); return $this; } /** * Create a new ability for a specific model. * * @param \Illuminate\Database\Eloquent\Model|string $model * @param string|array $attributes * @return static */ public static function createForModel($model, $attributes) { $model = static::makeForModel($model, $attributes); $model->save(); return $model; } /** * Make a new ability for a specific model. * * @param \Illuminate\Database\Eloquent\Model|string $model * @param string|array $attributes * @return static */ public static function makeForModel($model, $attributes) { if (is_string($attributes)) { $attributes = ['name' => $attributes]; } if ($model === '*') { return (new static)->forceFill($attributes + [ 'entity_type' => '*', ]); } if (is_string($model)) { $model = new $model; } return (new static)->forceFill($attributes + [ 'entity_type' => $model->getMorphClass(), 'entity_id' => $model->exists ? $model->getKey() : null, ]); } /** * The roles relationship. * * @return \Illuminate\Database\Eloquent\Relations\MorphToMany */ public function roles(): MorphToMany { $relation = $this->morphedByMany( Models::classname(Role::class), 'entity', Models::table('permissions') )->withPivot('forbidden', 'scope'); return Models::scope()->applyToRelation($relation); } /** * The users relationship. * * @return \Illuminate\Database\Eloquent\Relations\MorphToMany */ public function users(): MorphToMany { $relation = $this->morphedByMany( Models::classname(User::class), 'entity', Models::table('permissions') )->withPivot('forbidden', 'scope'); return Models::scope()->applyToRelation($relation); } /** * Get the identifier for this ability. * * @return string */ final public function getIdentifierAttribute() { $slug = $this->attributes['name']; if ($this->attributes['entity_type'] !== null) { $slug .= '-'.$this->attributes['entity_type']; } if ($this->attributes['entity_id'] !== null) { $slug .= '-'.$this->attributes['entity_id']; } if ($this->attributes['only_owned']) { $slug .= '-owned'; } return strtolower($slug); } /** * Get the ability's "slug" attribute. * * @return string */ public function getSlugAttribute() { return $this->getIdentifierAttribute(); } /** * Constrain a query to having the given name. * * @param \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder $query * @return string|array $name * @return bool $strict * @return void */ public function scopeByName($query, $name, $strict = false) { $names = (array) $name; if (! $strict && $name !== '*') { $names[] = '*'; } $query->whereIn("{$this->table}.name", $names); } /** * Constrain a query to simple abilities. * * @param \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder $query * @return void */ public function scopeSimpleAbility($query) { $query->whereNull("{$this->table}.entity_type"); } /** * Constrain a query to an ability for a specific model. * * @param \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder $query * @param \Illuminate\Database\Eloquent\Model|string $model * @param bool $strict * @return void */ public function scopeForModel($query, $model, $strict = false) { (new AbilitiesForModel)->constrain($query, $model, $strict); } } PK@ Ze.bouncer/src/Database/Concerns/Authorizable.phpnu[make(Clipboard::class) ->check($this, $ability, $model); } /** * Determine if the authority does not have a given ability. * * @param string $ability * @param \Illuminate\Database\Eloquent\Model|null $model * @return bool */ public function cant($ability, $model = null) { return ! $this->can($ability, $model); } /** * Determine if the authority does not have a given ability. * * @param string $ability * @param \Illuminate\Database\Eloquent\Model|null $model * @return bool */ public function cannot($ability, $model = null) { return $this->cant($ability, $model); } } PK@ Z=:V.bouncer/src/Database/Concerns/HasAbilities.phpnu[abilities()->detach(); } }); } /** * The abilities relationship. * * @return \Illuminate\Database\Eloquent\Relations\MorphToMany */ public function abilities(): MorphToMany { $relation = $this->morphToMany( Models::classname(Ability::class), 'entity', Models::table('permissions') )->withPivot('forbidden', 'scope'); return Models::scope()->applyToRelation($relation); } /** * Get all of the model's allowed abilities. * * @return \Illuminate\Database\Eloquent\Collection */ public function getAbilities() { return Container::getInstance() ->make(Clipboard::class) ->getAbilities($this); } /** * Get all of the model's allowed abilities. * * @return \Illuminate\Database\Eloquent\Collection */ public function getForbiddenAbilities() { return Container::getInstance() ->make(Clipboard::class) ->getAbilities($this, false); } /** * Give an ability to the model. * * @param mixed $ability * @param mixed|null $model * @return \Silber\Bouncer\Conductors\GivesAbilities|$this */ public function allow($ability = null, $model = null) { if (is_null($ability)) { return new GivesAbilities($this); } (new GivesAbilities($this))->to($ability, $model); return $this; } /** * Remove an ability from the model. * * @param mixed $ability * @param mixed|null $model * @return \Silber\Bouncer\Conductors\RemovesAbilities|$this */ public function disallow($ability = null, $model = null) { if (is_null($ability)) { return new RemovesAbilities($this); } (new RemovesAbilities($this))->to($ability, $model); return $this; } /** * Forbid an ability to the model. * * @param mixed $ability * @param mixed|null $model * @return \Silber\Bouncer\Conductors\ForbidsAbilities|$this */ public function forbid($ability = null, $model = null) { if (is_null($ability)) { return new ForbidsAbilities($this); } (new ForbidsAbilities($this))->to($ability, $model); return $this; } /** * Remove ability forbiddal from the model. * * @param mixed $ability * @param mixed|null $model * @return \Silber\Bouncer\Conductors\UnforbidsAbilities|$this */ public function unforbid($ability = null, $model = null) { if (is_null($ability)) { return new UnforbidsAbilities($this); } (new UnforbidsAbilities($this))->to($ability, $model); return $this; } } PK@ Z'*bouncer/src/Database/Scope/TenantScope.phpnu[applyToModelQuery($query, $model->getTable()); } } PK@ ZD$bouncer/src/Database/Scope/Scope.phpnu[scope = $id; return $this; } /** * Only scope relationships. Models should stay global. * * @param bool $boolean * @return $this */ public function onlyRelations($boolean = true) { $this->onlyScopeRelations = $boolean; return $this; } /** * Don't scope abilities granted to roles. * * The role <=> ability associations will be global. * * @return $this */ public function dontScopeRoleAbilities() { $this->scopeRoleAbilities = false; return $this; } /** * Append the tenant ID to the given cache key. * * @param string $key * @return string */ public function appendToCacheKey($key) { return is_null($this->scope) ? $key : $key.'-'.$this->scope; } /** * Scope the given model to the current tenant. * * @param \Illuminate\Database\Eloquent\Model $model * @return \Illuminate\Database\Eloquent\Model */ public function applyToModel(Model $model) { if (! $this->onlyScopeRelations && ! is_null($this->scope)) { $model->scope = $this->scope; } return $model; } /** * Scope the given model query to the current tenant. * * @param \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder $query * @param string|null $table * @return \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder */ public function applyToModelQuery($query, $table = null) { if ($this->onlyScopeRelations) { return $query; } if (is_null($table)) { $table = $query->getModel()->getTable(); } return $this->applyToQuery($query, $table); } /** * Scope the given relationship query to the current tenant. * * @param \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder $query * @param string $table * @return \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder */ public function applyToRelationQuery($query, $table) { return $this->applyToQuery($query, $table); } /** * Scope the given relation to the current tenant. * * @param \Illuminate\Database\Eloquent\Relations\BelongsToMany $relation * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany */ public function applyToRelation(BelongsToMany $relation) { $this->applyToRelationQuery( $relation->getQuery(), $relation->getTable() ); return $relation; } /** * Apply the current scope to the given query. * * This internal method does not check whether * the given query needs to be scoped. That * is fully the caller's responsibility. * * @param \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder $query * @param string $table * @return \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder */ protected function applyToQuery($query, $table) { return $query->where(function ($query) use ($table) { $query->whereNull("{$table}.scope"); if (! is_null($this->scope)) { $query->orWhere("{$table}.scope", $this->scope); } }); } /** * Get the current scope value. * * @return mixed */ public function get() { return $this->scope; } /** * Get the additional attributes for pivot table records. * * @param string|null $authority * @return array */ public function getAttachAttributes($authority = null) { if (is_null($this->scope)) { return []; } if (! $this->scopeRoleAbilities && $this->isRoleClass($authority)) { return []; } return ['scope' => $this->scope]; } /** * Run the given callback with the given temporary scope. * * @param mixed $scope * @param callable $callback * @return mixed */ public function onceTo($scope, callable $callback) { $mainScope = $this->scope; $this->scope = $scope; $result = $callback(); $this->scope = $mainScope; return $result; } /** * Remove the scope completely. * * @return $this */ public function remove() { $this->scope = null; } /** * Run the given callback without the scope applied. * * @param callable $callback * @return mixed */ public function removeOnce(callable $callback) { return $this->onceTo(null, $callback); } /** * Determine whether the given class name is the role model. * * @param string|null $className * @return bool */ protected function isRoleClass($className) { return Models::classname(Role::class) === $className; } } PK@ ZE-bouncer/src/Database/HasRolesAndAbilities.phpnu[roles = Helpers::toArray($roles); } /** * Remove the role from the given authority. * * @param \Illuminate\Database\Eloquent\Model|array|int $authority * @return void */ public function from($authority) { if (! ($roleIds = $this->getRoleIds())) { return; } $authorities = is_array($authority) ? $authority : [$authority]; foreach (Helpers::mapAuthorityByClass($authorities) as $class => $keys) { $this->retractRoles($roleIds, $class, $keys); } } /** * Get the IDs of anyexisting roles provided. * * @return array */ protected function getRoleIds() { list($models, $names) = Helpers::partition($this->roles, function ($role) { return $role instanceof Model; }); $ids = $models->map(function ($model) { return $model->getKey(); }); if ($names->count()) { $ids = $ids->merge($this->getRoleIdsFromNames($names->all())); } return $ids->all(); } /** * Get the IDs of the roles with the given names. * * @param string[] $names * @return \Illuminate\Database\Eloquent\Collection */ protected function getRoleIdsFromNames(array $names) { $key = Models::role()->getKeyName(); return Models::role() ->whereIn('name', $names) ->get([$key]) ->pluck($key); } /** * Retract the given roles from the given authorities. * * @param array $roleIds * @param string $authorityClass * @param array $authorityIds * @return void */ protected function retractRoles($roleIds, $authorityClass, $authorityIds) { $query = $this->newPivotTableQuery(); $morphType = (new $authorityClass)->getMorphClass(); foreach ($roleIds as $roleId) { foreach ($authorityIds as $authorityId) { $query->orWhere($this->getDetachQueryConstraint( $roleId, $authorityId, $morphType )); } } $query->delete(); } /** * Get a constraint for the detach query for the given parameters. * * @param mixed $roleId * @param mixed $authorityId * @param string $morphType * @return \Closure */ protected function getDetachQueryConstraint($roleId, $authorityId, $morphType) { return function ($query) use ($roleId, $authorityId, $morphType) { $query->where(Models::scope()->getAttachAttributes() + [ 'role_id' => $roleId, 'entity_id' => $authorityId, 'entity_type' => $morphType, ]); }; } /** * Get a query builder instance for the assigned roles pivot table. * * @return \Illuminate\Database\Query\Builder */ protected function newPivotTableQuery() { return Models::query('assigned_roles'); } } PK@ Za)-bouncer/src/Conductors/UnforbidsAbilities.phpnu[ true]; /** * Constructor. * * @param \Illuminate\Database\Eloquent\Model|string|null $authority */ public function __construct($authority = null) { $this->authority = $authority; } } PK@ Z +bouncer/src/Conductors/ForbidsAbilities.phpnu[authority = $authority; } } PK@ Z%q&bouncer/src/Conductors/ChecksRoles.phpnu[authority = $authority; $this->clipboard = $clipboard; } /** * Check if the authority has any of the given roles. * * @param string ...$roles * @return bool */ public function a(...$roles) { return $this->clipboard->checkRole($this->authority, $roles, 'or'); } /** * Check if the authority doesn't have any of the given roles. * * @param string ...$roles * @return bool */ public function notA(...$roles) { return $this->clipboard->checkRole($this->authority, $roles, 'not'); } /** * Alias to the "a" method. * * @param string ...$roles * @return bool */ public function an(...$roles) { return $this->clipboard->checkRole($this->authority, $roles, 'or'); } /** * Alias to the "notA" method. * * @param string ...$roles * @return bool */ public function notAn(...$roles) { return $this->clipboard->checkRole($this->authority, $roles, 'not'); } /** * Check if the authority has all of the given roles. * * @param string ...$roles * @return bool */ public function all(...$roles) { return $this->clipboard->checkRole($this->authority, $roles, 'and'); } } PK@ ZD+bouncer/src/Conductors/RemovesAbilities.phpnu[ false]; /** * Constructor. * * @param \Illuminate\Database\Eloquent\Model|string|null $authority */ public function __construct($authority = null) { $this->authority = $authority; } } PK@ ZwO1bouncer/src/Conductors/Lazy/ConductsAbilities.phpnu[conductor = $conductor; $this->abilities = $abilities; } /** * Sets that the abilities should be applied towards everything. * * @param array $attributes * @return void */ public function everything(array $attributes = []) { $this->everything = true; $this->attributes = $attributes; } /** * Destructor. * */ public function __destruct() { $this->conductor->to( $this->abilities, $this->everything ? '*' : null, $this->attributes ); } } PK@ Z(+M0bouncer/src/Conductors/Lazy/HandlesOwnership.phpnu[conductor = $conductor; $this->model = $model; $this->attributes = $attributes; } /** * Limit ownership to the given ability. * * @param string|string[] $ability * @param array $attributes * @return void */ public function to($ability, array $attributes = []) { $this->ability = $ability; $this->attributes = array_merge($this->attributes, $attributes); } /** * Destructor. * */ public function __destruct() { $this->conductor->to( $this->ability, $this->model, $this->attributes + ['only_owned' => true] ); } } PK@ ZɂbJ)bouncer/src/Conductors/GivesAbilities.phpnu[authority = $authority; } } PK@ ZV@1bouncer/src/Conductors/SyncsRolesAndAbilities.phpnu[authority = $authority; } /** * Sync the provided roles to the authority. * * @param iterable $roles * @return void */ public function roles($roles) { $this->sync( Models::role()->getRoleKeys($roles), $this->authority->roles() ); } /** * Sync the provided abilities to the authority. * * @param iterable $abilities * @return void */ public function abilities($abilities) { $this->syncAbilities($abilities); } /** * Sync the provided forbidden abilities to the authority. * * @param iterable $abilities * @return void */ public function forbiddenAbilities($abilities) { $this->syncAbilities($abilities, ['forbidden' => true]); } /** * Sync the given abilities for the authority. * * @param iterable $abilities * @param array $options * @return void */ protected function syncAbilities($abilities, $options = ['forbidden' => false]) { $abilityKeys = $this->getAbilityIds($abilities); $authority = $this->getAuthority(); $relation = $authority->abilities(); $this->newPivotQuery($relation) ->where('entity_type', $authority->getMorphClass()) ->whereNotIn($relation->getRelatedPivotKeyName(), $abilityKeys) ->where('forbidden', $options['forbidden']) ->delete(); if ($options['forbidden']) { (new ForbidsAbilities($this->authority))->to($abilityKeys); } else { (new GivesAbilities($this->authority))->to($abilityKeys); } } /** * Get (and cache) the authority for whom to sync roles/abilities. * * @return \Illuminate\Database\Eloquent\Model */ protected function getAuthority() { if (is_string($this->authority)) { $this->authority = Models::role()->firstOrCreate([ 'name' => $this->authority ]); } return $this->authority; } /** * Get the fully qualified column name for the abilities table's primary key. * * @return string */ protected function getAbilitiesQualifiedKeyName() { $model = Models::ability(); return $model->getTable().'.'.$model->getKeyName(); } /** * Sync the given IDs on the pivot relation. * * This is a heavily-modified version of Eloquent's built-in * BelongsToMany@sync - which we sadly cannot use because * our scope sets a "closure where" on the pivot table. * * @param array $ids * @param \Illuminate\Database\Eloquent\Relations\BelongsToMany $relation * @return void */ protected function sync(array $ids, BelongsToMany $relation) { $current = $this->pluck( $this->newPivotQuery($relation), $relation->getRelatedPivotKeyName() ); $this->detach(array_diff($current, $ids), $relation); $relation->attach( array_diff($ids, $current), Models::scope()->getAttachAttributes($this->authority) ); } /** * Detach the records with the given IDs from the relationship. * * @param array $ids * @param \Illuminate\Database\Eloquent\Relations\BelongsToMany $relation * @return void */ public function detach(array $ids, BelongsToMany $relation) { if (empty($ids)) { return; } $this->newPivotQuery($relation) ->whereIn($relation->getRelatedPivotKeyName(), $ids) ->delete(); } /** * Get a scoped query for the pivot table. * * @param \Illuminate\Database\Eloquent\Relations\BelongsToMany $relation * @return \Illuminate\Database\Query\Builder */ protected function newPivotQuery(BelongsToMany $relation) { $query = $relation ->newPivotStatement() ->where('entity_type', $this->getAuthority()->getMorphClass()) ->where( $relation->getForeignPivotKeyName(), $relation->getParent()->getKey() ); return Models::scope()->applyToRelationQuery( $query, $relation->getTable() ); } /** * Pluck the values of the given column using the provided query. * * @param mixed $query * @param string $column * @return string[] */ protected function pluck($query, $column) { return Arr::pluck($query->get([$column]), last(explode('.', $column))); } } PK@ ZC C 5bouncer/src/Conductors/Concerns/ConductsAbilities.phpnu[to('*', '*', $attributes); } /** * Allow/disallow all abilities on the given model. * * @param string|array|\Illuminate\Database\Eloquent\Model $models * @param array $attributes * @return void */ public function toManage($models, array $attributes = []) { if (is_array($models)) { foreach ($models as $model) { $this->to('*', $model, $attributes); } } else { $this->to('*', $models, $attributes); } } /** * Allow/disallow owning the given model. * * @param string|object $model * @param array $attributes * @return \Silber\Bouncer\Conductors\Lazy\HandlesOwnership */ public function toOwn($model, array $attributes = []) { return new Lazy\HandlesOwnership($this, $model, $attributes); } /** * Allow/disallow owning all models. * * @param array $attributes * @return \Silber\Bouncer\Conductors\Lazy\HandlesOwnership */ public function toOwnEverything(array $attributes = []) { return $this->toOwn('*', $attributes); } /** * Determines whether a call to "to" with the given parameters should be conducted lazily. * * @param mixed $abilities * @param mixed $model * @return bool */ protected function shouldConductLazy($abilities) { // We'll only create a lazy conductor if we got a single // param, and that single param is either a string or // a numerically-indexed array (of simple strings). if (func_num_args() > 1) { return false; } if (is_string($abilities)) { return true; } if (! is_array($abilities) || ! Helpers::isIndexedArray($abilities)) { return false; } return (new Collection($abilities))->every('is_string'); } /** * Create a lazy abilities conductor. * * @param string|string[] $ablities * @return \Silber\Bouncer\Conductors\Lazy\ConductsAbilities */ protected function conductLazy($abilities) { return new Lazy\ConductsAbilities($this, $abilities); } } PK@ Z*<bouncer/src/Conductors/Concerns/FindsAndCreatesAbilities.phpnu[getKey()]; } if ( ! is_null($model)) { return $this->getModelAbilityKeys($abilities, $model, $attributes); } if (Helpers::isAssociativeArray($abilities)) { return $this->getAbilityIdsFromMap($abilities, $attributes); } if (! is_array($abilities) && ! $abilities instanceof Collection) { $abilities = [$abilities]; } return $this->getAbilityIdsFromArray($abilities, $attributes); } /** * Get the ability IDs for the given map. * * The map should use the ['ability-name' => Entity::class] format. * * @param array $map * @param array $attributes * @return array */ protected function getAbilityIdsFromMap(array $map, array $attributes) { list($map, $list) = Helpers::partition($map, function ($value, $key) { return ! is_int($key); }); return $map->map(function ($entity, $ability) use ($attributes) { return $this->getAbilityIds($ability, $entity, $attributes); })->collapse()->merge($this->getAbilityIdsFromArray($list, $attributes))->all(); } /** * Get the ability IDs from the provided array, creating the ones that don't exist. * * @param iterable $abilities * @param array $attributes * @return array */ protected function getAbilityIdsFromArray($abilities, array $attributes) { $groups = Helpers::groupModelsAndIdentifiersByType($abilities); $keyName = Models::ability()->getKeyName(); $groups['strings'] = $this->abilitiesByName($groups['strings'], $attributes) ->pluck($keyName)->all(); $groups['models'] = Arr::pluck($groups['models'], $keyName); return Arr::collapse($groups); } /** * Get the abilities for the given model ability descriptors. * * @param array|string $abilities * @param \Illuminate\Database\Eloquent\Model|string|array $model * @param array $attributes * @return array */ protected function getModelAbilityKeys($abilities, $model, array $attributes) { $abilities = Collection::make(is_array($abilities) ? $abilities : [$abilities]); $models = Collection::make(is_array($model) ? $model : [$model]); return $abilities->map(function ($ability) use ($models, $attributes) { return $models->map(function ($model) use ($ability, $attributes) { return $this->getModelAbility($ability, $model, $attributes)->getKey(); }); })->collapse()->all(); } /** * Get an ability for the given entity. * * @param string $ability * @param \Illuminate\Database\Eloquent\Model|string $entity * @param array $attributes * @return \Silber\Bouncer\Database\Ability */ protected function getModelAbility($ability, $entity, array $attributes) { $entity = $this->getEntityInstance($entity); $existing = $this->findAbility($ability, $entity, $attributes); return $existing ?: $this->createAbility($ability, $entity, $attributes); } /** * Find the ability for the given entity. * * @param string $ability * @param \Illuminate\Database\Eloquent\Model|string $entity * @param array $attributes * @return \Silber\Bouncer\Database\Ability|null */ protected function findAbility($ability, $entity, $attributes) { $onlyOwned = isset($attributes['only_owned']) ? $attributes['only_owned'] : false; $query = Models::ability() ->where('name', $ability) ->forModel($entity, true) ->where('only_owned', $onlyOwned); return Models::scope()->applyToModelQuery($query)->first(); } /** * Create an ability for the given entity. * * @param string $ability * @param \Illuminate\Database\Eloquent\Model|string $entity * @param array $attributes * @return \Silber\Bouncer\Database\Ability */ protected function createAbility($ability, $entity, $attributes) { return Models::ability()->createForModel($entity, $attributes + [ 'name' => $ability, ]); } /** * Get an instance of the given model. * * @param \Illuminate\Database\Eloquent\Model|string $model * @return \Illuminate\Database\Eloquent\Model|string */ protected function getEntityInstance($model) { if ($model === '*') { return '*'; } if ( ! $model instanceof Model) { return new $model; } // Creating an ability for a non-existent model gives the authority that // ability on all instances of that model. If the developer passed in // a model instance that does not exist, it is probably a mistake. if ( ! $model->exists) { throw new InvalidArgumentException( 'The model does not exist. To edit access to all models, use the class name instead' ); } return $model; } /** * Get or create abilities by their name. * * @param array|string $abilities * @param array $attributes * @return \Illuminate\Support\Collection */ protected function abilitiesByName($abilities, $attributes = []) { $abilities = array_unique(is_array($abilities) ? $abilities : [$abilities]); if (empty($abilities)) { return new Collection; } $existing = Models::ability()->simpleAbility()->whereIn('name', $abilities)->get(); return $existing->merge($this->createMissingAbilities( $existing, $abilities, $attributes )); } /** * Create the non-existant abilities by name. * * @param \Illuminate\Database\Eloquent\Collection $existing * @param string[] $abilities * @param array $attributes * @return array */ protected function createMissingAbilities($existing, array $abilities, $attributes = []) { $missing = array_diff($abilities, $existing->pluck('name')->all()); return array_map(function ($ability) use ($attributes) { return Models::ability()->create($attributes + ['name' => $ability]); }, $missing); } } PK@ ZZhF:bouncer/src/Conductors/Concerns/DisassociatesAbilities.phpnu[shouldConductLazy(...func_get_args())) { return $this->conductLazy($abilities); } if ($ids = $this->getAbilityIds($abilities, $entity, $attributes)) { $this->disassociateAbilities($this->getAuthority(), $ids); } return true; } /** * Detach the given IDs from the authority. * * @param \Illuminate\Database\Eloquent\Model|null $authority * @param array $ids * @return void */ protected function disassociateAbilities($authority, array $ids) { if (is_null($authority)) { $this->disassociateEveryone($ids); } else { $this->disassociateAuthority($authority, $ids); } } /** * Disassociate the authority from the abilities with the given IDs. * * @param \Illuminate\Database\Eloquent\Model $authority * @param array $ids * @return void */ protected function disassociateAuthority(Model $authority, array $ids) { $this->getAbilitiesPivotQuery($authority, $ids) ->where($this->constraints()) ->delete(); } /** * Get the base abilities pivot query. * * @param \Illuminate\Database\Eloquent\Model $model * @param array $ids * @return \Illuminate\Database\Query\Builder */ protected function getAbilitiesPivotQuery(Model $model, $ids) { $relation = $model->abilities(); $query = $relation ->newPivotStatement() ->where($relation->getQualifiedForeignPivotKeyName(), $model->getKey()) ->where('entity_type', $model->getMorphClass()) ->whereIn($relation->getQualifiedRelatedPivotKeyName(), $ids); return Models::scope()->applyToRelationQuery( $query, $relation->getTable() ); } /** * Disassociate everyone from the abilities with the given IDs. * * @param array $ids * @return void */ protected function disassociateEveryone(array $ids) { $query = Models::query('permissions') ->whereNull('entity_id') ->where($this->constraints()) ->whereIn('ability_id', $ids); Models::scope()->applyToRelationQuery($query, $query->from); $query->delete(); } /** * Get the authority from which to disassociate the abilities. * * @return \Illuminate\Database\Eloquent\Model|null */ protected function getAuthority() { if (is_null($this->authority)) { return null; } if ($this->authority instanceof Model) { return $this->authority; } return Models::role()->where('name', $this->authority)->firstOrFail(); } /** * Get the additional constraints for the detaching query. * * @return array */ protected function constraints() { return property_exists($this, 'constraints') ? $this->constraints : []; } } PK@ Z-)7bouncer/src/Conductors/Concerns/AssociatesAbilities.phpnu[shouldConductLazy(...func_get_args())) { return $this->conductLazy($abilities); } $ids = $this->getAbilityIds($abilities, $model, $attributes); $this->associateAbilities($ids, $this->getAuthority()); } /** * Get the authority, creating a role authority if necessary. * * @return \Illuminate\Database\Eloquent\Model|null */ protected function getAuthority() { if (is_null($this->authority)) { return null; } if ($this->authority instanceof Model) { return $this->authority; } return Models::role()->firstOrCreate(['name' => $this->authority]); } /** * Get the IDs of the associated abilities. * * @param \Illuminate\Database\Eloquent\Model|null $authority * @param array $abilityIds * @return array */ protected function getAssociatedAbilityIds($authority, array $abilityIds) { if (is_null($authority)) { return $this->getAbilityIdsAssociatedWithEveryone($abilityIds); } $relation = $authority->abilities(); $table = Models::table('abilities'); $relation->whereIn("{$table}.id", $abilityIds) ->wherePivot('forbidden', '=', $this->forbidding); Models::scope()->applyToRelation($relation); return $relation->get(["{$table}.id"])->pluck('id')->all(); } /** * Get the IDs of the abilities associated with everyone. * * @param array $abilityIds * @return array */ protected function getAbilityIdsAssociatedWithEveryone(array $abilityIds) { $query = Models::query('permissions') ->whereNull('entity_id') ->whereIn('ability_id', $abilityIds) ->where('forbidden', '=', $this->forbidding); Models::scope()->applyToRelationQuery($query, $query->from); return Arr::pluck($query->get(['ability_id']), 'ability_id'); } /** * Associate the given ability IDs on the permissions table. * * @param array $ids * @param \Illuminate\Database\Eloquent\Model|null $authority * @return void */ protected function associateAbilities(array $ids, Model $authority = null) { $ids = array_diff($ids, $this->getAssociatedAbilityIds($authority, $ids, false)); if (is_null($authority)) { $this->associateAbilitiesToEveryone($ids); } else { $this->associateAbilitiesToAuthority($ids, $authority); } } /** * Associate these abilities with the given authority. * * @param array $ids * @param \Illuminate\Database\Eloquent\Model $authority * @return void */ protected function associateAbilitiesToAuthority(array $ids, Model $authority) { $attributes = Models::scope()->getAttachAttributes(get_class($authority)); $authority ->abilities() ->attach($ids, ['forbidden' => $this->forbidding] + $attributes); } /** * Associate these abilities with everyone. * * @param array $ids * @return void */ protected function associateAbilitiesToEveryone(array $ids) { $attributes = ['forbidden' => $this->forbidding]; $attributes += Models::scope()->getAttachAttributes(); $records = array_map(function ($id) use ($attributes) { return ['ability_id' => $id] + $attributes; }, $ids); Models::query('permissions')->insert($records); } } PK@ Z>Zk'bouncer/src/Conductors/AssignsRoles.phpnu[roles = Helpers::toArray($roles); } /** * Assign the roles to the given authority. * * @param \Illuminate\Database\Eloquent\Model|array|int $authority * @return bool */ public function to($authority) { $authorities = is_array($authority) ? $authority : [$authority]; $roles = Models::role()->findOrCreateRoles($this->roles); foreach (Helpers::mapAuthorityByClass($authorities) as $class => $ids) { $this->assignRoles($roles, $class, new Collection($ids)); } return true; } /** * Assign the given roles to the given authorities. * * @param \Illuminate\Support\Collection $roles * @param string $authorityClass * @param \Illuminate\Support\Collection $authorityIds * @return void */ protected function assignRoles(Collection $roles, $authorityClass, Collection $authorityIds) { $roleIds = $roles->map(function ($model) { return $model->getKey(); }); $morphType = (new $authorityClass)->getMorphClass(); $records = $this->buildAttachRecords($roleIds, $morphType, $authorityIds); $existing = $this->getExistingAttachRecords($roleIds, $morphType, $authorityIds); $this->createMissingAssignRecords($records, $existing); } /** * Get the pivot table records for the roles already assigned. * * @param \Illuminate\Support\Collection $roleIds * @param string $morphType * @param \Illuminate\Support\Collection $authorityIds * @return \Illuminate\Support\Collection */ protected function getExistingAttachRecords($roleIds, $morphType, $authorityIds) { $query = $this->newPivotTableQuery() ->whereIn('role_id', $roleIds->all()) ->whereIn('entity_id', $authorityIds->all()) ->where('entity_type', $morphType); Models::scope()->applyToRelationQuery($query, $query->from); return new Collection($query->get()); } /** * Build the raw attach records for the assigned roles pivot table. * * @param \Illuminate\Support\Collection $roleIds * @param string $morphType * @param \Illuminate\Support\Collection $authorityIds * @return \Illuminate\Support\Collection */ protected function buildAttachRecords($roleIds, $morphType, $authorityIds) { return $roleIds ->crossJoin($authorityIds) ->mapSpread(function ($roleId, $authorityId) use ($morphType) { return Models::scope()->getAttachAttributes() + [ 'role_id' => $roleId, 'entity_id' => $authorityId, 'entity_type' => $morphType, ]; }); } /** * Save the non-existing attach records in the DB. * * @param \Illuminate\Support\Collection $records * @param \Illuminate\Support\Collection $existing * @return void */ protected function createMissingAssignRecords(Collection $records, Collection $existing) { $existing = $existing->keyBy(function ($record) { return $this->getAttachRecordHash((array) $record); }); $records = $records->reject(function ($record) use ($existing) { return $existing->has($this->getAttachRecordHash($record)); }); $this->newPivotTableQuery()->insert($records->all()); } /** * Get a string identifying the given attach record. * * @param array $record * @return string */ protected function getAttachRecordHash(array $record) { return $record['role_id'].$record['entity_id'].$record['entity_type']; } /** * Get a query builder instance for the assigned roles pivot table. * * @return \Illuminate\Database\Query\Builder */ protected function newPivotTableQuery() { return Models::query('assigned_roles'); } } PK@ Z#bouncer/src/BaseClipboard.phpnu[checkGetId($authority, $ability, $model); } /** * Check if an authority has the given roles. * * @param \Illuminate\Database\Eloquent\Model $authority * @param array|string $roles * @param string $boolean * @return bool */ public function checkRole(Model $authority, $roles, $boolean = 'or') { $count = $this->countMatchingRoles($authority, $roles); if ($boolean == 'or') { return $count > 0; } elseif ($boolean === 'not') { return $count === 0; } return $count == count((array) $roles); } /** * Count the authority's roles matching the given roles. * * @param \Illuminate\Database\Eloquent\Model $authority * @param array|string $roles * @return int */ protected function countMatchingRoles($authority, $roles) { $lookups = $this->getRolesLookup($authority); return count(array_filter($roles, function ($role) use ($lookups) { switch (true) { case is_string($role): return $lookups['names']->has($role); case is_numeric($role): return $lookups['ids']->has($role); case $role instanceof Model: return $lookups['ids']->has($role->getKey()); } throw new InvalidArgumentException('Invalid model identifier'); })); } /** * Get the given authority's roles' IDs and names. * * @param \Illuminate\Database\Eloquent\Model $authority * @return array */ public function getRolesLookup(Model $authority) { $roles = $authority->roles()->get([ 'name', Models::role()->getQualifiedKeyName() ])->pluck('name', Models::role()->getKeyName()); return ['ids' => $roles, 'names' => $roles->flip()]; } /** * Get the given authority's roles' names. * * @param \Illuminate\Database\Eloquent\Model $authority * @return \Illuminate\Support\Collection */ public function getRoles(Model $authority) { return $this->getRolesLookup($authority)['names']->keys(); } /** * Get a list of the authority's abilities. * * @param \Illuminate\Database\Eloquent\Model $authority * @param bool $allowed * @return \Illuminate\Database\Eloquent\Collection */ public function getAbilities(Model $authority, $allowed = true) { return Abilities::forAuthority($authority, $allowed)->get(); } /** * Get a list of the authority's forbidden abilities. * * @param \Illuminate\Database\Eloquent\Model $authority * @return \Illuminate\Database\Eloquent\Collection */ public function getForbiddenAbilities(Model $authority) { return $this->getAbilities($authority, false); } /** * Determine whether the authority owns the given model. * * @return bool */ public function isOwnedBy($authority, $model) { return $model instanceof Model && Models::isOwnedBy($authority, $model); } } PK@ Z! bouncer/src/BouncerFacade.phpnu[isForbidden($authority, $ability, $model)) { return false; } $ability = $this->getAllowingAbility($authority, $ability, $model); return $ability ? $ability->getKey() : null; } /** * Determine whether the given ability request is explicitely forbidden. * * @param \Illuminate\Database\Eloquent\Model $authority * @param string $ability * @param \Illuminate\Database\Eloquent\Model|string|null $model * @return bool */ protected function isForbidden(Model $authority, $ability, $model = null) { return $this->getHasAbilityQuery( $authority, $ability, $model, $allowed = false )->exists(); } /** * Get the ability model that allows the given ability request. * * Returns null if the ability is not allowed. * * @param \Illuminate\Database\Eloquent\Model $authority * @param string $ability * @param \Illuminate\Database\Eloquent\Model|string|null $model * @return \Illuminate\Database\Eloquent\Model|null */ protected function getAllowingAbility(Model $authority, $ability, $model = null) { return $this->getHasAbilityQuery( $authority, $ability, $model, $allowed = true )->first(); } /** * Get the query for where the given authority has the given ability. * * @param \Illuminate\Database\Eloquent\Model $authority * @param string $ability * @param \Illuminate\Database\Eloquent\Model|string|null $model * @param bool $allowed * @return \Illuminate\Database\Eloquent\Builder */ protected function getHasAbilityQuery($authority, $ability, $model, $allowed) { $query = Abilities::forAuthority($authority, $allowed); if (! $this->isOwnedBy($authority, $model)) { $query->where('only_owned', false); } if (is_null($model)) { return $this->constrainToSimpleAbility($query, $ability); } return $query->byName($ability)->forModel($model); } /** * Constrain the query to the given non-model ability. * * @param \Illuminate\Database\Eloquent\Builder $query * @param string $ability * @return \Illuminate\Database\Eloquent\Builder */ protected function constrainToSimpleAbility($query, $ability) { return $query->where(function ($query) use ($ability) { $query->where('name', $ability)->whereNull('entity_type'); $query->orWhere(function ($query) use ($ability) { $query->where('name', '*')->where(function ($query) { $query->whereNull('entity_type')->orWhere('entity_type', '*'); }); }); }); } } PK@ ZR$DD$bouncer/src/Console/CleanCommand.phpnu[getComputedOptions(); if ($unassigned) { $this->deleteUnassignedAbilities(); } if ($orphaned) { $this->deleteOrphanedAbilities(); } } /** * Get the options to use, computed by omission. * * @return array */ protected function getComputedOptions() { $unassigned = $this->option('unassigned'); $orphaned = $this->option('orphaned'); if (! $unassigned && ! $orphaned) { $unassigned = $orphaned = true; } return [$unassigned, $orphaned]; } /** * Delete abilities not assigned to anyone. * * @return void */ protected function deleteUnassignedAbilities() { $query = $this->getUnassignedAbilitiesQuery(); if (($count = $query->count()) > 0) { $query->delete(); $this->info("Deleted {$count} unassigned ".Str::plural('ability', $count).'.'); } else { $this->info('No unassigned abilities.'); } } /** * Get the base query for all unassigned abilities. * * @return \Illuminate\Database\Eloquent\Query */ protected function getUnassignedAbilitiesQuery() { $model = Models::ability(); return $model->whereNotIn($model->getKeyName(), function ($query) { $query->from(Models::table('permissions'))->select('ability_id'); }); } /** * Delete model abilities whose models have been deleted. * * @return void */ protected function deleteOrphanedAbilities() { $query = $this->getBaseOrphanedQuery()->where(function ($query) { foreach ($this->getEntityTypes() as $entityType) { $query->orWhere(function ($query) use ($entityType) { $this->scopeQueryToWhereModelIsMissing($query, $entityType); }); } }); if (($count = $query->count()) > 0) { $query->delete(); $this->info("Deleted {$count} orphaned ".Str::plural('ability', $count).'.'); } else { $this->info('No orphaned abilities.'); } } /** * Scope the given query to where the ability's model is missing. * * @param \Illuminate\Database\Query\Builder $query * @param string $entityType * @return void */ protected function scopeQueryToWhereModelIsMissing($query, $entityType) { $model = $this->makeModel($entityType); $abilities = $this->abilitiesTable(); $query->where("{$abilities}.entity_type", $entityType); $query->whereNotIn("{$abilities}.entity_id", function ($query) use ($model) { $table = $model->getTable(); $query->from($table)->select($table.'.'.$model->getKeyName()); }); } /** * Get the entity types of all model abilities. * * @return iterable */ protected function getEntityTypes() { return $this ->getBaseOrphanedQuery() ->distinct() ->pluck('entity_type'); } /** * Get the base query for abilities with missing models. * * @return \Illuminate\Database\Eloquent\Builder */ protected function getBaseOrphanedQuery() { $table = $this->abilitiesTable(); return Models::ability() ->whereNotNull("{$table}.entity_id") ->where("{$table}.entity_type", '!=', '*'); } /** * Get the name of the abilities table. * * @return string */ protected function abilitiesTable() { return Models::ability()->getTable(); } /** * Get an instance of the model for the given entity type. * * @param string $entityType * @return string */ protected function makeModel($entityType) { $class = Relation::getMorphedModel($entityType) ?? $entityType; return new $class; } } PK@ Z,2;)bouncer/src/Contracts/CachedClipboard.phpnu[getKey()]]; } if ($model instanceof Collection) { $keys = $model->map(function ($model) { return $model->getKey(); }); return [$model->first(), $keys]; } } /** * Fill the given array with the given value for any missing keys. * * @param iterable $array * @param mixed $value * @param iterable $keys * @return iterable */ public static function fillMissingKeys($array, $value, $keys) { foreach ($keys as $key) { if (! array_key_exists($key, $array)) { $array[$key] = $value; } } return $array; } /** * Group models and their identifiers by type (models, strings & integers). * * @param iterable $models * @return array */ public static function groupModelsAndIdentifiersByType($models) { $groups = (new Collection($models))->groupBy(function ($model) { if (is_numeric($model)) { return 'integers'; } else if (is_string($model)) { return 'strings'; } else if ($model instanceof Model) { return 'models'; } throw new InvalidArgumentException('Invalid model identifier'); })->map(function ($items) { return $items->all(); })->all(); return static::fillMissingKeys($groups, [], ['integers', 'strings', 'models']); } /** * Determines if an array is associative. * * An array is "associative" if it doesn't have sequential numerical keys beginning with zero. * * @param mixed $array * @return bool */ public static function isAssociativeArray($array) { if (! is_array($array)) { return false; } $keys = array_keys($array); return array_keys($keys) !== $keys; } /** * Determines if an array is numerically indexed. * * @param mixed $array * @return bool */ public static function isIndexedArray($array) { if (! is_array($array)) { return false; } foreach ($array as $key => $value) { if (! is_numeric($key)) { return false; } } return true; } /** * Determines whether the given model is set to soft delete. * * @param \Illuminate\Database\Eloquent\Model $model * @return bool */ public static function isSoftDeleting(Model $model) { // Soft deleting models is controlled by adding the SoftDeletes trait // to the model. Instead of recursively looking for that trait, we // will check for the existence of the `isForceDeleting` method. if (! method_exists($model, 'isForceDeleting')) { return false; } return ! $model->isForceDeleting(); } /** * Convert the given value to an array. * * @param mixed $value * @return array */ public static function toArray($value) { if (is_array($value)) { return $value; } if ($value instanceof Collection) { return $value->all(); } return [$value]; } /** * Map a list of authorities by their class name. * * @param array $authorities * @return array */ public static function mapAuthorityByClass(array $authorities) { $map = []; foreach ($authorities as $authority) { if ($authority instanceof Model) { $map[get_class($authority)][] = $authority->getKey(); } else { $map[Models::classname(User::class)][] = $authority; } } return $map; } /** * Partition the given collection into two collection using the given callback. * * @param iterable $items * @param callable $callback * @return \Illuminate\Support\Collection */ public static function partition($items, callable $callback) { $partitions = [new Collection, new Collection]; foreach ($items as $key => $item) { $partitions[(int) ! $callback($item, $key)][$key] = $item; } return new Collection($partitions); } } PK@ ZS9_bouncer/src/Guard.phpnu[clipboard = $clipboard; } /** * Get the clipboard instance. * * @return \Silber\Bouncer\Contracts\Clipboard */ public function getClipboard() { return $this->clipboard; } /** * Set the clipboard instance. * * @param \Silber\Bouncer\Contracts\Clipboard $clipboard * @return $this */ public function setClipboard(Contracts\Clipboard $clipboard) { $this->clipboard = $clipboard; return $this; } /** * Determine whether the clipboard used is a cached clipboard. * * @return bool */ public function usesCachedClipboard() { return $this->clipboard instanceof Contracts\CachedClipboard; } /** * Set or get which slot to run the clipboard's checks. * * @param string|null $slot * @return $this|string */ public function slot($slot = null) { if (is_null($slot)) { return $this->slot; } if (! in_array($slot, ['before', 'after'])) { throw new InvalidArgumentException( "{$slot} is an invalid gate slot" ); } $this->slot = $slot; return $this; } /** * Register the clipboard at the given gate. * * @param \Illuminate\Contracts\Auth\Access\Gate $gate * @return $this */ public function registerAt(Gate $gate) { $gate->before(function () { return $this->runBeforeCallback(...func_get_args()); }); $gate->after(function () { return $this->runAfterCallback(...func_get_args()); }); return $this; } /** * Run the gate's "before" callback. * * @param \Illuminate\Database\Eloquent\Model $authority * @param string $ability * @param mixed $arguments * @param mixed $additional * @return bool|null */ protected function runBeforeCallback($authority, $ability, $arguments = []) { if ($this->slot != 'before') { return; } if (count($arguments) > 2) { return; } $model = isset($arguments[0]) ? $arguments[0] : null; return $this->checkAtClipboard($authority, $ability, $model); } /** * Run the gate's "before" callback. * * @param \Illuminate\Database\Eloquent\Model $authority * @param string $ability * @param mixed $result * @param array $arguments * @return bool|null */ protected function runAfterCallback($authority, $ability, $result, $arguments = []) { if (! is_null($result)) { return $result; } if ($this->slot != 'after') { return; } if (count($arguments) > 2) { return; } $model = isset($arguments[0]) ? $arguments[0] : null; return $this->checkAtClipboard($authority, $ability, $model); } /** * Run an auth check at the clipboard. * * @param \Illuminate\Database\Eloquent\Model $authority * @param string $ability * @param \Illuminate\Database\Eloquent\Model|string|null $model * @return mixed */ protected function checkAtClipboard(Model $authority, $ability, $model) { if ($id = $this->clipboard->checkGetId($authority, $ability, $model)) { return $this->allow('Bouncer granted permission via ability #'.$id); } // If the response from "checkGetId" is "false", then this ability // has been explicity forbidden. We'll return false so the gate // doesn't run any further checks. Otherwise we return null. return $id; } } PK@ ZߜQ3Q3bouncer/src/Bouncer.phpnu[guard = $guard; } /** * Create a new Bouncer instance. * * @param mixed $user * @return static */ public static function create($user = null) { return static::make($user)->create(); } /** * Create a bouncer factory instance. * * @param mixed $user * @return \Silber\Bouncer\Factory */ public static function make($user = null) { return new Factory($user); } /** * Start a chain, to allow the given authority an ability. * * @param \Illuminate\Database\Eloquent\Model|string $authority * @return \Silber\Bouncer\Conductors\GivesAbilities */ public function allow($authority) { return new Conductors\GivesAbilities($authority); } /** * Start a chain, to allow everyone an ability. * * @return \Silber\Bouncer\Conductors\GivesAbilities */ public function allowEveryone() { return new Conductors\GivesAbilities(); } /** * Start a chain, to disallow the given authority an ability. * * @param \Illuminate\Database\Eloquent\Model|string $authority * @return \Silber\Bouncer\Conductors\RemovesAbilities */ public function disallow($authority) { return new Conductors\RemovesAbilities($authority); } /** * Start a chain, to disallow everyone the an ability. * * @return \Silber\Bouncer\Conductors\RemovesAbilities */ public function disallowEveryone() { return new Conductors\RemovesAbilities(); } /** * Start a chain, to forbid the given authority an ability. * * @param \Illuminate\Database\Eloquent\Model|string $authority * @return \Silber\Bouncer\Conductors\GivesAbilities */ public function forbid($authority) { return new Conductors\ForbidsAbilities($authority); } /** * Start a chain, to forbid everyone an ability. * * @return \Silber\Bouncer\Conductors\GivesAbilities */ public function forbidEveryone() { return new Conductors\ForbidsAbilities(); } /** * Start a chain, to unforbid the given authority an ability. * * @param \Illuminate\Database\Eloquent\Model|string $authority * @return \Silber\Bouncer\Conductors\RemovesAbilities */ public function unforbid($authority) { return new Conductors\UnforbidsAbilities($authority); } /** * Start a chain, to unforbid an ability from everyone. * * @return \Silber\Bouncer\Conductors\RemovesAbilities */ public function unforbidEveryone() { return new Conductors\UnforbidsAbilities(); } /** * Start a chain, to assign the given role to a model. * * @param \Silber\Bouncer\Database\Role|\Illuminate\Support\Collection|string $roles * @return \Silber\Bouncer\Conductors\AssignsRoles */ public function assign($roles) { return new Conductors\AssignsRoles($roles); } /** * Start a chain, to retract the given role from a model. * * @param \Illuminate\Support\Collection|\Silber\Bouncer\Database\Role|string $roles * @return \Silber\Bouncer\Conductors\RemovesRoles */ public function retract($roles) { return new Conductors\RemovesRoles($roles); } /** * Start a chain, to sync roles/abilities for the given authority. * * @param \Illuminate\Database\Eloquent\Model|string $authority * @return \Silber\Bouncer\Conductors\SyncsRolesAndAbilities */ public function sync($authority) { return new Conductors\SyncsRolesAndAbilities($authority); } /** * Start a chain, to check if the given authority has a certain role. * * @param \Illuminate\Database\Eloquent\Model $authority * @return \Silber\Bouncer\Conductors\ChecksRoles */ public function is(Model $authority) { return new Conductors\ChecksRoles($authority, $this->getClipboard()); } /** * Get the clipboard instance. * * @return \Silber\Bouncer\Contracts\Clipboard */ public function getClipboard() { return $this->guard->getClipboard(); } /** * Set the clipboard instance used by bouncer. * * Will also register the given clipboard with the container. * * @param \Silber\Bouncer\Contracts\Clipboard * @return $this */ public function setClipboard(Contracts\Clipboard $clipboard) { $this->guard->setClipboard($clipboard); return $this->registerClipboardAtContainer(); } /** * Register the guard's clipboard at the container. * * @return $this */ public function registerClipboardAtContainer() { $clipboard = $this->guard->getClipboard(); Container::getInstance()->instance(Contracts\Clipboard::class, $clipboard); return $this; } /** * Use a cached clipboard with the given cache instance. * * @param \Illuminate\Contracts\Cache\Store $cache * @return $this */ public function cache(Store $cache = null) { $cache = $cache ?: $this->resolve(CacheRepository::class)->getStore(); if ($this->usesCachedClipboard()) { $this->guard->getClipboard()->setCache($cache); return $this; } return $this->setClipboard(new CachedClipboard($cache)); } /** * Fully disable all query caching. * * @return $this */ public function dontCache() { return $this->setClipboard(new Clipboard); } /** * Clear the cache. * * @param null|\Illuminate\Database\Eloquent\Model $authority * @return $this */ public function refresh(Model $authority = null) { if ($this->usesCachedClipboard()) { $this->getClipboard()->refresh($authority); } return $this; } /** * Clear the cache for the given authority. * * @param \Illuminate\Database\Eloquent\Model $authority * @return $this */ public function refreshFor(Model $authority) { if ($this->usesCachedClipboard()) { $this->getClipboard()->refreshFor($authority); } return $this; } /** * Set the access gate instance. * * @param \Illuminate\Contracts\Auth\Access\Gate $gate * @return $this */ public function setGate(Gate $gate) { $this->gate = $gate; return $this; } /** * Get the gate instance. * * @return \Illuminate\Contracts\Auth\Access\Gate|null */ public function getGate() { return $this->gate; } /** * Get the gate instance. Throw if not set. * * @return \Illuminate\Contracts\Auth\Access\Gate * * @throws \RuntimeException */ public function gate() { if (is_null($this->gate)) { throw new RuntimeException('The gate instance has not been set.'); } return $this->gate; } /** * Determine whether the clipboard used is a cached clipboard. * * @return bool */ public function usesCachedClipboard() { return $this->guard->usesCachedClipboard(); } /** * Define a new ability using a callback. * * @param string $ability * @param callable|string $callback * @return $this * * @throws \InvalidArgumentException */ public function define($ability, $callback) { return $this->gate()->define($ability, $callback); } /** * Determine if the given ability should be granted for the current user. * * @param string $ability * @param array|mixed $arguments * @return \Illuminate\Auth\Access\Response * * @throws \Illuminate\Auth\Access\AuthorizationException */ public function authorize($ability, $arguments = []) { return $this->gate()->authorize($ability, $arguments); } /** * Determine if the given ability is allowed. * * @param string $ability * @param array|mixed $arguments * @return bool */ public function can($ability, $arguments = []) { return $this->gate()->allows($ability, $arguments); } /** * Determine if any of the given abilities are allowed. * * @param array $abilities * @param array|mixed $arguments * @return bool */ public function canAny($abilities, $arguments = []) { return $this->gate()->any($abilities, $arguments); } /** * Determine if the given ability is denied. * * @param string $ability * @param array|mixed $arguments * @return bool */ public function cannot($ability, $arguments = []) { return $this->gate()->denies($ability, $arguments); } /** * Determine if the given ability is allowed. * * Alias for the "can" method. * * @deprecated * @param string $ability * @param array|mixed $arguments * @return bool */ public function allows($ability, $arguments = []) { return $this->can($ability, $arguments); } /** * Determine if the given ability is denied. * * Alias for the "cannot" method. * * @deprecated * @param string $ability * @param array|mixed $arguments * @return bool */ public function denies($ability, $arguments = []) { return $this->cannot($ability, $arguments); } /** * Get an instance of the role model. * * @param array $attributes * @return \Silber\Bouncer\Database\Role */ public function role(array $attributes = []) { return Models::role($attributes); } /** * Get an instance of the ability model. * * @param array $attributes * @return \Silber\Bouncer\Database\Ability */ public function ability(array $attributes = []) { return Models::ability($attributes); } /** * Set Bouncer to run its checks before the policies. * * @param bool $boolean * @return $this */ public function runBeforePolicies($boolean = true) { $this->guard->slot($boolean ? 'before' : 'after'); return $this; } /** * Register an attribute/callback to determine if a model is owned by a given authority. * * @param string|\Closure $model * @param string|\Closure|null $attribute * @return $this */ public function ownedVia($model, $attribute = null) { Models::ownedVia($model, $attribute); return $this; } /** * Set the model to be used for abilities. * * @param string $model * @return $this */ public function useAbilityModel($model) { Models::setAbilitiesModel($model); return $this; } /** * Set the model to be used for roles. * * @param string $model * @return $this */ public function useRoleModel($model) { Models::setRolesModel($model); return $this; } /** * Set the model to be used for users. * * @param string $model * @return $this */ public function useUserModel($model) { Models::setUsersModel($model); return $this; } /** * Set custom table names. * * @param array $map * @return $this */ public function tables(array $map) { Models::setTables($map); return $this; } /** * Get the model scoping instance. * * @param \Silber\Bouncer\Contracts\Scope|null $scope * @return mixed */ public function scope(Scope $scope = null) { return Models::scope($scope); } /** * Resolve the given type from the container. * * @param string $abstract * @param array $parameters * @return mixed */ protected function resolve($abstract, array $parameters = []) { return Container::getInstance()->make($abstract, $parameters); } } PK@ Z!bouncer/src/Constraints/Group.phpnu[ */ protected $constraints; /** * The logical operator to use when checked after a previous constrainer. * * @var string */ protected $logicalOperator = 'and'; /** * Constructor. * * @param iterable<\Silber\Bouncer\Constraints\Constrainer> $constraints */ public function __construct($constraints = []) { $this->constraints = new Collection($constraints); } /** * Create a new "and" group. * * @return static */ public static function withAnd() { return new static; } /** * Create a new "and" group. * * @return static */ public static function withOr() { return (new static)->logicalOperator('or'); } /** * Create a new instance from the raw data. * * @param array $data * @return static */ public static function fromData(array $data) { $group = new static(array_map(function ($data) { return $data['class']::fromData($data['params']); }, $data['constraints'])); return $group->logicalOperator($data['logicalOperator']); } /** * Add the given constraint to the list of constraints. * * @param \Silber\Bouncer\Constraints\Constrainer $constraint */ public function add(Constrainer $constraint) { $this->constraints->push($constraint); return $this; } /** * Determine whether the given entity/authority passes this constraint. * * @param \Illuminate\Database\Eloquent\Model $entity * @param \Illuminate\Database\Eloquent\Model|null $authority * @return bool */ public function check(Model $entity, Model $authority = null) { if ($this->constraints->isEmpty()) { return true; } return $this->constraints->reduce(function ($result, $constraint) use ($entity, $authority) { $passes = $constraint->check($entity, $authority); if (is_null($result)) { return $passes; } return $constraint->isOr() ? ($result || $passes) : ($result && $passes); }); } /** * Set the logical operator to use when checked after a previous constrainer. * * @param string|null $operator * @return $this|string */ public function logicalOperator($operator = null) { if (is_null($operator)) { return $this->logicalOperator; } Helpers::ensureValidLogicalOperator($operator); $this->logicalOperator = $operator; return $this; } /** * Checks whether the logical operator is an "and" operator. * * @param string $operator */ public function isAnd() { return $this->logicalOperator == 'and'; } /** * Checks whether the logical operator is an "and" operator. * * @param string $operator */ public function isOr() { return $this->logicalOperator == 'or'; } /** * Determine whether the given constrainer is equal to this object. * * @param \Silber\Bouncer\Constraints\Constrainer $constrainer * @return bool */ public function equals(Constrainer $constrainer) { if (! $constrainer instanceof static) { return false; } if ($this->constraints->count() != $constrainer->constraints->count()) { return false; } foreach ($this->constraints as $index => $constraint) { if (! $constrainer->constraints[$index]->equals($constraint)) { return false; } } return true; } /** * Get the JSON-able data of this object. * * @return array */ public function data() { return [ 'class' => static::class, 'params' => [ 'logicalOperator' => $this->logicalOperator, 'constraints' => $this->constraints->map(function ($constraint) { return $constraint->data(); })->all(), ], ]; } } PK@ Zq7OO+bouncer/src/Constraints/ValueConstraint.phpnu[column = $column; $this->operator = $operator; $this->value = $value; } /** * Determine whether the given entity/authority passed this constraint. * * @param \Illuminate\Database\Eloquent\Model $entity * @param \Illuminate\Database\Eloquent\Model|null $authority * @return bool */ public function check(Model $entity, Model $authority = null) { return $this->compare($entity->{$this->column}, $this->value); } /** * Create a new instance from the raw data. * * @param array $data * @return static */ public static function fromData(array $data) { $constraint = new static( $data['column'], $data['operator'], $data['value'] ); return $constraint->logicalOperator($data['logicalOperator']); } /** * Get the JSON-able data of this object. * * @return array */ public function data() { return [ 'class' => static::class, 'params' => [ 'column' => $this->column, 'operator' => $this->operator, 'value' => $this->value, 'logicalOperator' => $this->logicalOperator, ], ]; } } PK@ Zburr,bouncer/src/Constraints/ColumnConstraint.phpnu[a = $a; $this->operator = $operator; $this->b = $b; } /** * Determine whether the given entity/authority passes the constraint. * * @param \Illuminate\Database\Eloquent\Model $entity * @param \Illuminate\Database\Eloquent\Model|null $authority * @return bool */ public function check(Model $entity, Model $authority = null) { if (is_null($authority)) { return false; } return $this->compare($entity->{$this->a}, $authority->{$this->b}); } /** * Create a new instance from the raw data. * * @param array $data * @return static */ public static function fromData(array $data) { $constraint = new static( $data['a'], $data['operator'], $data['b'] ); return $constraint->logicalOperator($data['logicalOperator']); } /** * Get the JSON-able data of this object. * * @return array */ public function data() { return [ 'class' => static::class, 'params' => [ 'a' => $this->a, 'operator' => $this->operator, 'b' => $this->b, 'logicalOperator' => $this->logicalOperator, ], ]; } } PK@ Z~b,R R #bouncer/src/Constraints/Builder.phpnu[ */ protected $constraints; /** * Constructor. * */ public function __construct() { $this->constraints = new Collection; } /** * Create a new builder instance. * * @return static */ public static function make() { return new static; } /** * Add a "where" constraint. * * @param string|\Closure $column * @param mixed $operator * @param mixed $value * @return $this */ public function where($column, $operator = null, $value = null) { if ($column instanceof Closure) { return $this->whereNested('and', $column); } return $this->addConstraint(Constraint::where(...func_get_args())); } /** * Add an "or where" constraint. * * @param string|\Closure $column * @param mixed $operator * @param mixed $value * @return $this */ public function orWhere($column, $operator = null, $value = null) { if ($column instanceof Closure) { return $this->whereNested('or', $column); } return $this->addConstraint(Constraint::orWhere(...func_get_args())); } /** * Add a "where column" constraint. * * @param string $a * @param mixed $operator * @param mixed $b * @return $this */ public function whereColumn($a, $operator, $b = null) { return $this->addConstraint(Constraint::whereColumn(...func_get_args())); } /** * Add an "or where column" constraint. * * @param string $a * @param mixed $operator * @param mixed $b * @return $this */ public function orWhereColumn($a, $operator, $b = null) { return $this->addConstraint(Constraint::orWhereColumn(...func_get_args())); } /** * Build the compiled list of constraints. * * @return \Silber\Bouncer\Constraints\Constrainer */ public function build() { if ($this->constraints->count() == 1) { return $this->constraints->first(); } return new Group($this->constraints); } /** * Add a nested "where" clause. * * @param string $logicalOperator 'and'|'or' * @param \Closure $callback * @return $this */ protected function whereNested($logicalOperator, Closure $callback) { $callback($builder = new static); $constraint = $builder->build()->logicalOperator($logicalOperator); return $this->addConstraint($constraint); } /** * Add a constraint to the list of constraints. * * @param \Silber\Bouncer\Constraints\Constrainer $constraint * @return $this */ protected function addConstraint($constraint) { $this->constraints[] = $constraint; return $this; } } PK@ ZW""&bouncer/src/Constraints/Constraint.phpnu[logicalOperator('or'); } /** * Create a new constraint for where a column matches the given column on the authority. * * @param string $a * @param mixed $operator * @param mixed $b * @return \Silber\Bouncer\Constraints\ColumnConstraint */ public static function whereColumn($a, $operator, $b = null) { list($operator, $b) = static::prepareOperatorAndValue( $operator, $b, func_num_args() === 2 ); return new ColumnConstraint($a, $operator, $b); } /** * Create a new constraint for where a column matches the given column on the authority, * with the logical operator set to "or". * * @param string $a * @param mixed $operator * @param mixed $b * @return \Silber\Bouncer\Constraints\ColumnConstraint */ public static function orWhereColumn($a, $operator, $b = null) { return static::whereColumn(...func_get_args())->logicalOperator('or'); } /** * Set the logical operator to use when checked after a previous constraint. * * @param string|null $operator * @return $this|string */ public function logicalOperator($operator = null) { if (is_null($operator)) { return $this->logicalOperator; } Helpers::ensureValidLogicalOperator($operator); $this->logicalOperator = $operator; return $this; } /** * Checks whether the logical operator is an "and" operator. * * @param string $operator */ public function isAnd() { return $this->logicalOperator == 'and'; } /** * Checks whether the logical operator is an "and" operator. * * @param string $operator */ public function isOr() { return $this->logicalOperator == 'or'; } /** * Determine whether the given constrainer is equal to this object. * * @param \Silber\Bouncer\Constraints\Constrainer $constrainer * @return bool */ public function equals(Constrainer $constrainer) { if (! $constrainer instanceof static) { return false; } return $this->data()['params'] == $constrainer->data()['params']; } /** * Prepare the value and operator. * * @param string $operator * @param string $value * @param bool $usesDefault * @return array * * @throws \InvalidArgumentException */ protected static function prepareOperatorAndValue($operator, $value, $usesDefault) { if ($usesDefault) { return ['=', $operator]; } if (! in_array($operator, ['=', '==', '!=', '<', '>', '<=', '>='])) { throw new InvalidArgumentException("{$operator} is not a valid operator"); } return [$operator, $value]; } /** * Compare the two values by the constraint's operator. * * @param mixed $a * @param mixed $b * @return bool */ protected function compare($a, $b) { switch ($this->operator) { case '=': case '==': return $a == $b; case '!=': return $a != $b; case '<': return $a < $b; case '>': return $a > $b; case '<=': return $a <= $b; case '>=': return $a >= $b; } } } PK@ Z'bouncer/src/Constraints/Constrainer.phpnu[registerBouncer(); $this->registerCommands(); } /** * Bootstrap any application services. * * @return void */ public function boot() { $this->registerMorphs(); $this->setUserModel(); $this->registerAtGate(); if ($this->app->runningInConsole()) { $this->publishMiddleware(); $this->publishMigrations(); } } /** * Register Bouncer as a singleton. * * @return void */ protected function registerBouncer() { $this->app->singleton(Bouncer::class, function ($app) { return Bouncer::make() ->withClipboard(new CachedClipboard(new ArrayStore)) ->withGate($app->make(Gate::class)) ->create(); }); } /** * Register Bouncer's commands with artisan. * * @return void */ protected function registerCommands() { $this->commands(CleanCommand::class); } /** * Register Bouncer's models in the relation morph map. * * @return void */ protected function registerMorphs() { Models::updateMorphMap(); } /** * Publish the package's middleware. * * @return void */ protected function publishMiddleware() { $stub = __DIR__.'/../middleware/ScopeBouncer.php'; $target = app_path('Http/Middleware/ScopeBouncer.php'); $this->publishes([$stub => $target], 'bouncer.middleware'); } /** * Publish the package's migrations. * * @return void */ protected function publishMigrations() { if (class_exists('CreateBouncerTables')) { return; } $timestamp = date('Y_m_d_His', time()); $stub = __DIR__.'/../migrations/create_bouncer_tables.php'; $target = $this->app->databasePath().'/migrations/'.$timestamp.'_create_bouncer_tables.php'; $this->publishes([$stub => $target], 'bouncer.migrations'); } /** * Set the classname of the user model to be used by Bouncer. * * @return void */ protected function setUserModel() { if ($model = $this->getUserModel()) { Models::setUsersModel($model); } } /** * Get the user model from the application's auth config. * * @return string|null */ protected function getUserModel() { $config = $this->app->make('config'); if (is_null($guard = $config->get('auth.defaults.guard'))) { return null; } if (is_null($provider = $config->get("auth.guards.{$guard}.provider"))) { return null; } $model = $config->get("auth.providers.{$provider}.model"); // The standard auth config that ships with Laravel references the // Eloquent User model in the above config path. However, users // are free to reference anything there - so we check first. if (is_subclass_of($model, EloquentModel::class)) { return $model; } } /** * Register the bouncer's clipboard at the gate. * * @return void */ protected function registerAtGate() { // When creating a Bouncer instance thru the Factory class, it'll // auto-register at the gate. We already registered Bouncer in // the container using the Factory, so now we'll resolve it. $this->app->make(Bouncer::class); } } PK@ ZqU  bouncer/src/Factory.phpnu[user = $user; } /** * Create an instance of Bouncer. * * @return \Silber\Bouncer\Bouncer */ public function create() { $gate = $this->getGate(); $guard = $this->getGuard(); $bouncer = (new Bouncer($guard))->setGate($gate); if ($this->registerAtGate) { $guard->registerAt($gate); } if ($this->registerAtContainer) { $bouncer->registerClipboardAtContainer(); } return $bouncer; } /** * Set the cache instance to use for the clipboard. * * @param \Illuminate\Contracts\Cache\Store $cache * @return $this */ public function withCache(Store $cache) { $this->cache = $cache; return $this; } /** * Set the instance of the clipboard to use. * * @param \Silber\Bouncer\Contracts\Clipboard $clipboard * @return $this */ public function withClipboard(Contracts\Clipboard $clipboard) { $this->clipboard = $clipboard; return $this; } /** * Set the gate instance to use. * * @param \Illuminate\Contracts\Auth\Access\Gate $gate * @return $this */ public function withGate(GateContract $gate) { $this->gate = $gate; return $this; } /** * Set the user model to use for the gate. * * @param mixed $user * @return $this */ public function withUser($user) { $this->user = $user; return $this; } /** * Set whether the factory registers the clipboard instance with the container. * * @param bool $bool * @return $this */ public function registerClipboardAtContainer($bool = true) { $this->registerAtContainer = $bool; return $this; } /** * Set whether the factory registers the guard instance with the gate. * * @param bool $bool * @return $this */ public function registerAtGate($bool = true) { $this->registerAtGate = $bool; return $this; } /** * Get an instance of the clipboard. * * @return \Silber\Bouncer\Guard */ protected function getGuard() { return new Guard($this->getClipboard()); } /** * Get an instance of the clipboard. * * @return \Silber\Bouncer\Contracts\Clipboard */ protected function getClipboard() { return $this->clipboard ?: new CachedClipboard($this->getCacheStore()); } /** * Get an instance of the cache store. * * @return \Illuminate\Contracts\Cache\Store */ protected function getCacheStore() { return $this->cache ?: new ArrayStore; } /** * Get an instance of the gate. * * @return \Illuminate\Contracts\Auth\Access\Gate */ protected function getGate() { if ($this->gate) { return $this->gate; } return new Gate(Container::getInstance(), function () { return $this->user; }); } } PK@ ZE_55bouncer/LICENSE.txtnu[The MIT License (MIT) Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK@ Zbp p ,bouncer/migrations/create_bouncer_tables.phpnu[bigIncrements('id'); $table->string('name'); $table->string('title')->nullable(); $table->bigInteger('entity_id')->unsigned()->nullable(); $table->string('entity_type')->nullable(); $table->boolean('only_owned')->default(false); $table->json('options')->nullable(); $table->integer('scope')->nullable()->index(); $table->timestamps(); }); Schema::create(Models::table('roles'), function (Blueprint $table) { $table->bigIncrements('id'); $table->string('name'); $table->string('title')->nullable(); $table->integer('scope')->nullable()->index(); $table->timestamps(); $table->unique( ['name', 'scope'], 'roles_name_unique' ); }); Schema::create(Models::table('assigned_roles'), function (Blueprint $table) { $table->bigIncrements('id'); $table->bigInteger('role_id')->unsigned()->index(); $table->bigInteger('entity_id')->unsigned(); $table->string('entity_type'); $table->bigInteger('restricted_to_id')->unsigned()->nullable(); $table->string('restricted_to_type')->nullable(); $table->integer('scope')->nullable()->index(); $table->index( ['entity_id', 'entity_type', 'scope'], 'assigned_roles_entity_index' ); $table->foreign('role_id') ->references('id')->on(Models::table('roles')) ->onUpdate('cascade')->onDelete('cascade'); }); Schema::create(Models::table('permissions'), function (Blueprint $table) { $table->bigIncrements('id'); $table->bigInteger('ability_id')->unsigned()->index(); $table->bigInteger('entity_id')->unsigned()->nullable(); $table->string('entity_type')->nullable(); $table->boolean('forbidden')->default(false); $table->integer('scope')->nullable()->index(); $table->index( ['entity_id', 'entity_type', 'scope'], 'permissions_entity_index' ); $table->foreign('ability_id') ->references('id')->on(Models::table('abilities')) ->onUpdate('cascade')->onDelete('cascade'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop(Models::table('permissions')); Schema::drop(Models::table('assigned_roles')); Schema::drop(Models::table('roles')); Schema::drop(Models::table('abilities')); } } PK@ ZKpX X bouncer/migrations/sql/MySQL.sqlnu[create table `abilities` ( `id` int unsigned not null auto_increment primary key, `name` varchar(255) not null, `title` varchar(255) null, `entity_id` int unsigned null, `entity_type` varchar(255) null, `only_owned` tinyint(1) not null default '0', `options` json null, `scope` int null, `created_at` timestamp null, `updated_at` timestamp null ) default character set utf8mb4 collate utf8mb4_unicode_ci; alter table `abilities` add index `abilities_scope_index`(`scope`); create table `roles` ( `id` int unsigned not null auto_increment primary key, `name` varchar(255) not null, `title` varchar(255) null, `level` int unsigned null, `scope` int null, `created_at` timestamp null, `updated_at` timestamp null ) default character set utf8mb4 collate utf8mb4_unicode_ci; alter table `roles` add unique `roles_name_unique`(`name`, `scope`); alter table `roles` add index `roles_scope_index`(`scope`); create table `assigned_roles` ( `id` int unsigned not null auto_increment primary key, `role_id` int unsigned not null, `entity_id` int unsigned not null, `entity_type` varchar(255) not null, `restricted_to_id` int unsigned null, `restricted_to_type` varchar(255) null, `scope` int null ) default character set utf8mb4 collate utf8mb4_unicode_ci; alter table `assigned_roles` add index `assigned_roles_entity_index`(`entity_id`, `entity_type`, `scope`); alter table `assigned_roles` add constraint `assigned_roles_role_id_foreign` foreign key (`role_id`) references `roles` (`id`) on delete cascade on update cascade; alter table `assigned_roles` add index `assigned_roles_role_id_index`(`role_id`); alter table `assigned_roles` add index `assigned_roles_scope_index`(`scope`); create table `permissions` ( `id` int unsigned not null auto_increment primary key, `ability_id` int unsigned not null, `entity_id` int unsigned null, `entity_type` varchar(255) null, `forbidden` tinyint(1) not null default '0', `scope` int null ) default character set utf8mb4 collate utf8mb4_unicode_ci; alter table `permissions` add index `permissions_entity_index`(`entity_id`, `entity_type`, `scope`); alter table `permissions` add constraint `permissions_ability_id_foreign` foreign key (`ability_id`) references `abilities` (`id`) on delete cascade on update cascade; alter table `permissions` add index `permissions_ability_id_index`(`ability_id`); alter table `permissions` add index `permissions_scope_index`(`scope`); PK@ Z#bouncer/middleware/ScopeBouncer.phpnu[bouncer = $bouncer; } /** * Set the proper Bouncer scope for the incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { // Here you may use whatever mechanism you use in your app // to determine the current tenant. To demonstrate, the // $tenantId is set here from the user's account_id. $tenantId = $request->user()->account_id; $this->bouncer->scope()->to($tenantId); return $next($request); } } PK@ Zbouncer/composer.jsonnu[PK@ ZKP&P&Fbouncer/src/CachedClipboard.phpnu[PK@ Z5%-bouncer/src/Database/Titles/Title.phpnu[PK@ Zʆ)3bouncer/src/Database/Titles/RoleTitle.phpnu[PK@ Znmm,>5bouncer/src/Database/Titles/AbilityTitle.phpnu[PK@ Z VM M &Nbouncer/src/Database/Queries/Roles.phpnu[PK@ Z]ي*Xbouncer/src/Database/Queries/Abilities.phpnu[PK@ Z{ { 2mbouncer/src/Database/Queries/AbilitiesForModel.phpnu[PK@ ZI"m k{bouncer/src/Database/Ability.phpnu[PK@ Z(#~bouncer/src/Database/Role.phpnu[PK@ Z}ZZbouncer/src/Database/Models.phpnu[PK@ Z^?R!!*,bouncer/src/Database/Concerns/HasRoles.phpnu[PK@ ZH=Z(bouncer/src/Database/Concerns/IsRole.phpnu[PK@ ZN+bouncer/src/Database/Concerns/IsAbility.phpnu[PK@ Ze."bouncer/src/Database/Concerns/Authorizable.phpnu[PK@ Z=:V.bouncer/src/Database/Concerns/HasAbilities.phpnu[PK@ Z'*bouncer/src/Database/Scope/TenantScope.phpnu[PK@ ZD$Zbouncer/src/Database/Scope/Scope.phpnu[PK@ ZE-sbouncer/src/Database/HasRolesAndAbilities.phpnu[PK@ Z(lV 'bouncer/src/Conductors/RemovesRoles.phpnu[PK@ Za)-!bouncer/src/Conductors/UnforbidsAbilities.phpnu[PK@ Z +$bouncer/src/Conductors/ForbidsAbilities.phpnu[PK@ Z%q&'bouncer/src/Conductors/ChecksRoles.phpnu[PK@ ZD+40bouncer/src/Conductors/RemovesAbilities.phpnu[PK@ ZwO13bouncer/src/Conductors/Lazy/ConductsAbilities.phpnu[PK@ Z(+M0[9bouncer/src/Conductors/Lazy/HandlesOwnership.phpnu[PK@ ZɂbJ)?bouncer/src/Conductors/GivesAbilities.phpnu[PK@ ZV@1Bbouncer/src/Conductors/SyncsRolesAndAbilities.phpnu[PK@ ZC C 59Xbouncer/src/Conductors/Concerns/ConductsAbilities.phpnu[PK@ Z*<bbouncer/src/Conductors/Concerns/FindsAndCreatesAbilities.phpnu[PK@ ZZhF:Ibouncer/src/Conductors/Concerns/DisassociatesAbilities.phpnu[PK@ Z-)7xbouncer/src/Conductors/Concerns/AssociatesAbilities.phpnu[PK@ Z>Zk'obouncer/src/Conductors/AssignsRoles.phpnu[PK@ Z#abouncer/src/BaseClipboard.phpnu[PK@ Z! bouncer/src/BouncerFacade.phpnu[PK@ Zo- - bouncer/src/Clipboard.phpnu[PK@ ZR$DD$Gbouncer/src/Console/CleanCommand.phpnu[PK@ Z,2;)bouncer/src/Contracts/CachedClipboard.phpnu[PK@ Z#bouncer/src/Contracts/Clipboard.phpnu[PK@ Z|Nv v bouncer/src/Contracts/Scope.phpnu[PK@ Z.?  bouncer/src/Helpers.phpnu[PK@ ZS9_bouncer/src/Guard.phpnu[PK@ ZߜQ3Q3.bouncer/src/Bouncer.phpnu[PK@ Z!Vbbouncer/src/Constraints/Group.phpnu[PK@ Zq7OO+Ttbouncer/src/Constraints/ValueConstraint.phpnu[PK@ Zburr,{bouncer/src/Constraints/ColumnConstraint.phpnu[PK@ Z~b,R R #̃bouncer/src/Constraints/Builder.phpnu[PK@ ZW""&qbouncer/src/Constraints/Constraint.phpnu[PK@ Z'bouncer/src/Constraints/Constrainer.phpnu[PK@ Z3婤& bouncer/src/BouncerServiceProvider.phpnu[PK@ ZqU  Tbouncer/src/Factory.phpnu[PK@ ZE_55bouncer/LICENSE.txtnu[PK@ Zbp p ,bouncer/migrations/create_bouncer_tables.phpnu[PK@ ZKpX X bouncer/migrations/sql/MySQL.sqlnu[PK@ Z#bouncer/middleware/ScopeBouncer.phpnu[PK77