1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-12 22:22:32 +01:00

Merge pull request #8292 from turbo124/v5-develop

Add excludable permissions
This commit is contained in:
David Bomba 2023-02-21 07:51:16 +11:00 committed by GitHub
commit 57d6c13150
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 399 additions and 372 deletions

View File

@ -15,6 +15,7 @@ use App\Models\Account;
use App\Models\BankIntegration;
use App\Models\BankTransaction;
use App\Models\BankTransactionRule;
use App\Models\Client;
use App\Models\CompanyGateway;
use App\Models\Design;
use App\Models\ExpenseCategory;
@ -62,12 +63,41 @@ class BaseController extends Controller
*/
public $forced_index = 'data';
/**
* The calling controller Model Type
*/
protected $entity_type;
/**
* The calling controller Transformer type
*
*/
protected $entity_transformer;
/**
* The serializer in use with Fractal
*
*/
protected $serializer;
/* Grouped permissions when we want to hide columns for particular permission groups*/
private array $client_exclusion_fields = ['balance', 'paid_to_date', 'credit_balance', 'client_hash'];
private array $client_excludable_permissions = ['view_client'];
private array $client_excludable_overrides = ['edit_client', 'edit_all', 'view_invoice', 'view_all', 'edit_invoice'];
/* Grouped permissions when we want to hide columns for particular permission groups*/
/**
* Fractal manager.
* @var object
* @var Manager $manager
*/
protected Manager $manager;
/**
* An array of includes to be loaded by default.
*/
private $first_load = [
'account',
'user.company_user',
@ -120,6 +150,10 @@ class BaseController extends Controller
'company.task_schedulers',
];
/**
* An array of includes to be loaded by default
* when the company is large.
*/
private $mini_load = [
'account',
'user.company_user',
@ -139,12 +173,23 @@ class BaseController extends Controller
'company.bank_transaction_rules',
'company.task_schedulers',
];
/**
* __construct
*
* @return void
*/
public function __construct()
{
$this->manager = new Manager();
}
/**
* Initializes the Manager and transforms
* the required includes
*
* @return void
*/
private function buildManager()
{
$include = '';
@ -172,8 +217,7 @@ class BaseController extends Controller
}
/**
* Catch all fallback route
* for non-existant route.
* Catch all fallback route.
*/
public function notFound()
{
@ -187,7 +231,7 @@ class BaseController extends Controller
* end user has the correct permissions to
* view the includes
*
* @param array $includes The includes for the object
* @param string $includes The includes for the object
* @return string The filtered array of includes
*/
private function filterIncludes(string $includes): string
@ -227,9 +271,10 @@ class BaseController extends Controller
/**
* API Error response.
* @param string $message The return error message
* @param int $httpErrorCode 404/401/403 etc
* @return Response The JSON response
*
* @param string $message The return error message
* @param int $httpErrorCode 404/401/403 etc
* @return Response The JSON response
* @throws BindingResolutionException
*/
protected function errorResponse($message, $httpErrorCode = 400)
@ -245,7 +290,8 @@ class BaseController extends Controller
/**
* Refresh API response with latest cahnges
* @param Builer $query
*
* @param Builder $query
* @property App\Models\User auth()->user()
* @return Builer
*/
@ -286,6 +332,10 @@ class BaseController extends Controller
$query->where('clients.user_id', $user->id)->orWhere('clients.assigned_user_id', $user->id);
});
}
if ($user->hasExcludedPermissions($this->client_excludable_permissions, $this->client_excludable_overrides)) {
$query->exclude($this->client_exclusion_fields);
}
},
'company.company_gateways' => function ($query) use ($user) {
$query->whereNotNull('updated_at')->with('gateway');
@ -481,12 +531,14 @@ class BaseController extends Controller
);
if ($query instanceof Builder) {
//27-10-2022 - enforce unsigned integer
$limit = $this->resolveQueryLimit();
$paginator = $query->paginate($limit);
$query = $paginator->getCollection();
$resource = new Collection($query, $transformer, $this->entity_type);
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
} else {
$resource = new Collection($query, $transformer, $this->entity_type);
@ -494,8 +546,13 @@ class BaseController extends Controller
return $this->response($this->manager->createData($resource)->toArray());
}
private function resolveQueryLimit()
/**
* Returns the per page limit for the query.
*
* @return int
*/
private function resolveQueryLimit(): int
{
if (request()->has('per_page')) {
return abs((int)request()->input('per_page', 20));
@ -503,7 +560,13 @@ class BaseController extends Controller
return 20;
}
/**
* Mini Load Query
*
* @param mixed $query
* @return void
*/
protected function miniLoadResponse($query)
{
$user = auth()->user();
@ -587,6 +650,7 @@ class BaseController extends Controller
* able to access multiple companies, then we
* need to pass back the mini load only
*
* @deprecated
* @return bool
*/
private function complexPermissionsUser(): bool
@ -598,7 +662,13 @@ class BaseController extends Controller
return false;
}
/**
* Passes back the miniloaded data response
*
* @param mixed $query
* @return void
*/
protected function timeConstrainedResponse($query)
{
$user = auth()->user();
@ -637,6 +707,10 @@ class BaseController extends Controller
$query->where('clients.user_id', $user->id)->orWhere('clients.assigned_user_id', $user->id);
});
}
if ($user->hasExcludedPermissions($this->client_excludable_permissions, $this->client_excludable_overrides)) {
$query->exclude($this->client_exclusion_fields);
}
},
'company.company_gateways' => function ($query) use ($user) {
$query->whereNotNull('created_at')->with('gateway');
@ -833,7 +907,13 @@ class BaseController extends Controller
return $this->response($this->manager->createData($resource)->toArray());
}
/**
* List response
*
* @param mixed $query
* @return void
*/
protected function listResponse($query)
{
$this->buildManager();
@ -847,7 +927,6 @@ class BaseController extends Controller
$query->with($includes);
if (auth()->user() && ! auth()->user()->hasPermission('view_'.Str::snake(class_basename($this->entity_type)))) {
//06-10-2022 - some entities do not have assigned_user_id - this becomes an issue when we have a large company and low permission users
if (in_array($this->entity_type, [User::class])) {
$query->where('id', auth()->user()->id);
} elseif (in_array($this->entity_type, [BankTransactionRule::class,CompanyGateway::class, TaxRate::class, BankIntegration::class, Scheduler::class, BankTransaction::class, Webhook::class, ExpenseCategory::class])) { //table without assigned_user_id
@ -863,7 +942,10 @@ class BaseController extends Controller
$query->where('user_id', '=', auth()->user()->id)->orWhere('assigned_user_id', auth()->user()->id);
}
}
// $query->exclude(['balance','credit_balance','paid_to_date']);
if ($this->entity_type == Client::class && auth()->user()->hasExcludedPermissions($this->client_excludable_permissions, $this->client_excludable_overrides)) {
$query->exclude($this->client_exclusion_fields);
}
if (request()->has('updated_at') && request()->input('updated_at') > 0) {
$query->where('updated_at', '>=', date('Y-m-d H:i:s', intval(request()->input('updated_at'))));
@ -885,7 +967,13 @@ class BaseController extends Controller
return $this->response($this->manager->createData($resource)->toArray());
}
/**
* Sorts the response by keys
*
* @param mixed $response
* @return void
*/
protected function response($response)
{
$index = request()->input('index') ?: $this->forced_index;
@ -916,7 +1004,13 @@ class BaseController extends Controller
return response()->make($response, 200, $headers);
}
/**
* Item Response
*
* @param mixed $item
* @return void
*/
protected function itemResponse($item)
{
$this->buildManager();
@ -935,8 +1029,13 @@ class BaseController extends Controller
return $this->response($this->manager->createData($resource)->toArray());
}
public static function getApiHeaders()
/**
* Returns the API headers.
*
* @return array
*/
public static function getApiHeaders(): array
{
return [
'Content-Type' => 'application/json',
@ -944,8 +1043,14 @@ class BaseController extends Controller
'X-App-Version' => config('ninja.app_version'),
];
}
protected function getRequestIncludes($data)
/**
* Returns the parsed relationship includes
*
* @param mixed $data
* @return array
*/
protected function getRequestIncludes($data): array
{
/*
* Thresholds for displaying large account on first load
@ -971,7 +1076,12 @@ class BaseController extends Controller
return $data;
}
/**
* Main entrypoint for the default / route.
*
* @return mixed
*/
public function flutterRoute()
{
if ((bool) $this->checkAppSetup() !== false && $account = Account::first()) {
@ -1036,7 +1146,12 @@ class BaseController extends Controller
return redirect('/setup');
}
/**
* Sets the Flutter build to serve
*
* @return void
*/
private function setBuild()
{
$build = '';
@ -1064,7 +1179,13 @@ class BaseController extends Controller
return 'main.foss.dart.js';
}
}
/**
* Checks in a account has a required feature
*
* @param mixed $feature
* @return bool
*/
public function checkFeature($feature)
{
if (auth()->user()->account->hasFeature($feature)) {
@ -1073,7 +1194,12 @@ class BaseController extends Controller
return false;
}
/**
* Feature failure response
*
* @return mixed
*/
public function featureFailure()
{
return response()->json(['message' => 'Upgrade to a paid plan for this feature.'], 403);

View File

@ -41,10 +41,20 @@ class ContactHashLoginController extends Controller
{
return redirect($this->setRedirectPath());
}
/**
* Generic error page for client portal.
*
* @return void
*/
public function errorPage()
{
return render('generic.error', ['title' => session()->get('title'), 'notification' => session()->get('notification'), 'account' => auth()->guard('contact')?->user()?->user?->account, 'company' => auth()->guard('contact')?->user()?->user?->company]);
return render('generic.error', [
'title' => session()->get('title'),
'notification' => session()->get('notification'),
'account' => auth()->guard('contact')?->user()?->user?->account,
'company' => auth()->guard('contact')?->user()?->user?->company
]);
}
private function setRedirectPath()

View File

@ -75,8 +75,8 @@ class UserController extends BaseController
* tags={"users"},
* summary="Gets a list of users",
* description="Lists users, search and filters allow fine grained lists to be generated.
Query parameters can be added to performed more fine grained filtering of the users, these are handled by the UserFilters class which defines the methods available",
*
*Query parameters can be added to performed more fine grained filtering of the users, these are handled by the UserFilters class which defines the methods available",
* @OA\Parameter(ref="#/components/parameters/X-API-TOKEN"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),

View File

@ -30,7 +30,7 @@ class ValidCompanyQuantity implements Rule
return auth()->user()->company()->account->companies->count() < 10;
}
return auth()->user()->company()->account->companies->count() < auth()->user()->company()->account->hosted_company_count;
return auth()->user()->account->isPaid() && auth()->user()->company()->account->companies->count() < 10 ;
}
/**

View File

@ -17,14 +17,10 @@ use App\Jobs\Mail\NinjaMailerObject;
use App\Libraries\MultiDB;
use App\Mail\Admin\EntityFailedSendObject;
use App\Utils\Traits\Notifications\UserNotifies;
use Illuminate\Bus\Queueable;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class InvoiceFailedEmailNotification
{
use UserNotifies, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
use UserNotifies;
public $delay = 7;

View File

@ -11,14 +11,12 @@
namespace App\Models;
use App\Models\Traits\Excludable;
use Illuminate\Database\Eloquent\SoftDeletes;
class BankIntegration extends BaseModel
{
use SoftDeletes;
use Filterable;
use Excludable;
protected $fillable = [
'bank_account_name',

View File

@ -11,15 +11,16 @@
namespace App\Models;
use App\DataMapper\ClientSettings;
use App\Jobs\Util\WebhookHandler;
use Illuminate\Support\Str;
use Illuminate\Support\Carbon;
use App\Utils\Traits\MakesHash;
use App\Jobs\Util\WebhookHandler;
use App\Models\Traits\Excludable;
use App\DataMapper\ClientSettings;
use Illuminate\Database\Eloquent\Model;
use App\Utils\Traits\UserSessionAttributes;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
/**
* Class BaseModel
@ -33,6 +34,7 @@ class BaseModel extends Model
use MakesHash;
use UserSessionAttributes;
use HasFactory;
use Excludable;
protected $appends = [
'hashed_id',

View File

@ -28,6 +28,10 @@ class CompanyGateway extends BaseModel
'deleted_at' => 'timestamp',
];
protected $with = [
'gateway',
];
protected $fillable = [
'gateway_key',
'accepted_credit_cards',

View File

@ -12,12 +12,14 @@
namespace App\Models;
use App\Utils\Traits\MakesHash;
use App\Models\Traits\Excludable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException;
class StaticModel extends Model
{
use MakesHash;
use Excludable;
protected $casts = [
'updated_at' => 'timestamp',

View File

@ -392,8 +392,7 @@ class User extends Authenticatable implements MustVerifyEmail
}
}
return $this->isOwner() ||
$this->isAdmin() ||
return $this->isSuperUser() ||
(stripos($this->token()->cu->permissions, $permission) !== false) ||
(stripos($this->token()->cu->permissions, $all_permission) !== false) ||
(stripos($this->token()->cu->permissions, $edit_all) !== false) ||
@ -410,7 +409,7 @@ class User extends Authenticatable implements MustVerifyEmail
* @param string $permission '["view_all"]'
* @return boolean
*/
public function hasExactPermission(string $permission = '___'): bool
public function hasExactPermissionAndAll(string $permission = '___'): bool
{
$parts = explode('_', $permission);
$all_permission = '__';
@ -436,7 +435,7 @@ class User extends Authenticatable implements MustVerifyEmail
public function hasIntersectPermissions(array $permissions = []): bool
{
foreach ($permissions as $permission) {
if ($this->hasExactPermission($permission)) {
if ($this->hasExactPermissionAndAll($permission)) {
return true;
}
}
@ -444,6 +443,22 @@ class User extends Authenticatable implements MustVerifyEmail
return false;
}
/**
* Used when we need to match exactly what permission
* the user has, and not aggregate owner and admins.
*
* This method is used when we need to scope down the query
* and display a limited subset.
*
* @param string $permission '["view_all"]'
* @return boolean
*/
public function hasExactPermission(string $permission = '___'): bool
{
return (stripos($this->token()->cu->permissions, $permission) !== false);
}
/**
* Used when we need to match a range of permissions
* the user
@ -461,6 +476,44 @@ class User extends Authenticatable implements MustVerifyEmail
}
foreach ($permissions as $permission) {
if ($this->hasExactPermissionAndAll($permission)) {
return true;
}
}
return false;
}
/**
* Used when we need to filter permissions carefully.
*
* For instance, users that have view_client permissions should not
* see the client balance, however if they also have
* view_invoice or view_all etc, then they should be able to see the balance.
*
* First we pass over the excluded permissions and return false if we find a match.
*
* If those permissions are not hit, then we can iterate through the matched_permissions and search for a hit.
*
* Note, returning FALSE here means the user does NOT have the permission we want to exclude
*
* @param array $matched_permission
* @param array $excluded_permissions
* @return bool
*/
public function hasExcludedPermissions(array $matched_permission = [], array $excluded_permissions = []): bool
{
if ($this->isSuperUser()) {
return false;
}
foreach ($excluded_permissions as $permission) {
if ($this->hasExactPermission($permission)) {
return false;
}
}
foreach($matched_permission as $permission) {
if ($this->hasExactPermission($permission)) {
return true;
}
@ -469,7 +522,6 @@ class User extends Authenticatable implements MustVerifyEmail
return false;
}
public function documents()
{
return $this->morphMany(Document::class, 'documentable');

View File

@ -1,52 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Providers;
use App\Utils\CssInlinerPlugin;
use Illuminate\Support\ServiceProvider;
class MailCssInlinerServiceProvider extends ServiceProvider
{
// Thanks to @fedeisas/laravel-mail-css-inliner
/**
* Bootstrap the application events.
*
* @return void
*/
public function boot()
{
$this->publishes([
__DIR__.'/../config/css-inliner.php' => base_path('config/css-inliner.php'),
], 'config');
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
// $this->app->singleton(CssInlinerPlugin::class, function ($app) {
// return new CssInlinerPlugin([]);
// });
// $this->app->singleton(CssInlinerPlugin::class, function ($app) {
// return new CssInlinerPlugin([]);
// });
// $this->app->bind(CssInlinerPlugin::class, function ($app) {
// return new CssInlinerPlugin([]);
// });
}
}

View File

@ -1,140 +0,0 @@
<?php
namespace App\Utils;
use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles;
class CssInlinerPlugin implements \Swift_Events_SendListener
{
/**
* @var CssToInlineStyles
*/
protected $converter;
/**
* @var string[]
*/
protected $cssCache;
/**
* @var array
*/
protected $options;
/**
* @param array $options options defined in the configuration file.
*/
public function __construct(array $options)
{
$this->converter = new CssToInlineStyles();
$this->options = $options;
}
/**
* @param \Swift_Events_SendEvent $evt
*/
public function beforeSendPerformed(\Swift_Events_SendEvent $evt)
{
$message = $evt->getMessage();
if ($message->getContentType() === 'text/html'
|| ($message->getContentType() === 'multipart/alternative' && $message->getBody())
|| ($message->getContentType() === 'multipart/mixed' && $message->getBody())
) {
[$body, $cssResources] = $this->messageSieve($message->getBody());
$css = $this->concatCss($cssResources);
$message->setBody($this->converter->convert($body, $css));
}
foreach ($message->getChildren() as $part) {
if (strpos($part->getContentType(), 'text/html') === 0) {
[$body, $cssResources] = $this->messageSieve($part->getBody());
$css = $this->concatCss($cssResources);
$part->setBody($this->converter->convert($body, $css));
}
}
}
/**
* Do nothing
*
* @param \Swift_Events_SendEvent $evt
*/
public function sendPerformed(\Swift_Events_SendEvent $evt)
{
// Do Nothing
}
protected function concatCss(array $cssResources): string
{
$output = '';
foreach ($cssResources as $cssResource) {
$output .= $this->fetchCss($cssResource);
}
return $output;
}
protected function fetchCss(string $filename): string
{
if (isset($this->cssCache[$filename])) {
return $this->cssCache[$filename];
}
$fixedFilename = $filename;
// Fix relative protocols on hrefs. Assume https.
if (substr($filename, 0, 2) === '//') {
$fixedFilename = 'https:'.$filename;
}
$content = file_get_contents($fixedFilename);
if (! $content) {
return '';
}
$this->cssCache[$filename] = $content;
return $content;
}
protected function messageSieve(string $message): array
{
$cssResources = [];
// Initialize with config defaults, if any
if (isset($this->options['css-files'])) {
$cssResources = $this->options['css-files'];
}
$dom = new \DOMDocument();
// set error level
$internalErrors = libxml_use_internal_errors(true);
$dom->loadHTML($message);
// Restore error level
libxml_use_internal_errors($internalErrors);
$link_tags = $dom->getElementsByTagName('link');
/** @var \DOMElement $link */
foreach ($link_tags as $link) {
if ($link->getAttribute('rel') === 'stylesheet') {
array_push($cssResources, $link->getAttribute('href'));
}
}
$link_tags = $dom->getElementsByTagName('link');
for ($i = $link_tags->length; --$i >= 0;) {
$link = $link_tags->item($i);
if ($link->getAttribute('rel') === 'stylesheet') {
$link->parentNode->removeChild($link);
}
}
if (count($cssResources)) {
return [$dom->saveHTML(), $cssResources];
}
return [$message, []];
}
}

View File

@ -87,7 +87,6 @@
"symfony/http-client": "^6.0",
"symfony/mailgun-mailer": "^6.1",
"symfony/postmark-mailer": "^6.1",
"tijsverkoyen/css-to-inline-styles": "^2.2",
"turbo124/beacon": "^1.3",
"twilio/sdk": "^6.40",
"webpatser/laravel-countries": "dev-master#75992ad",

189
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "55a62c4290c3a07d0c38c8e4aeea61e0",
"content-hash": "6156afb5e414466a8cd7b5903bbcbc6a",
"packages": [
{
"name": "afosto/yaac",
@ -379,16 +379,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.258.10",
"version": "3.259.0",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "e164c9c5faf3965bd27ff2a4e9b7b3048ba8a337"
"reference": "096711644ebe5c956a97ef449fa2cb3b66443c25"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/e164c9c5faf3965bd27ff2a4e9b7b3048ba8a337",
"reference": "e164c9c5faf3965bd27ff2a4e9b7b3048ba8a337",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/096711644ebe5c956a97ef449fa2cb3b66443c25",
"reference": "096711644ebe5c956a97ef449fa2cb3b66443c25",
"shasum": ""
},
"require": {
@ -467,9 +467,9 @@
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.258.10"
"source": "https://github.com/aws/aws-sdk-php/tree/3.259.0"
},
"time": "2023-02-14T19:21:16+00:00"
"time": "2023-02-17T19:21:38+00:00"
},
{
"name": "bacon/bacon-qr-code",
@ -1504,28 +1504,27 @@
},
{
"name": "doctrine/lexer",
"version": "2.1.0",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/lexer.git",
"reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124"
"reference": "84a527db05647743d50373e0ec53a152f2cde568"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124",
"reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124",
"url": "https://api.github.com/repos/doctrine/lexer/zipball/84a527db05647743d50373e0ec53a152f2cde568",
"reference": "84a527db05647743d50373e0ec53a152f2cde568",
"shasum": ""
},
"require": {
"doctrine/deprecations": "^1.0",
"php": "^7.1 || ^8.0"
"php": "^8.1"
},
"require-dev": {
"doctrine/coding-standard": "^9 || ^10",
"phpstan/phpstan": "^1.3",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"doctrine/coding-standard": "^10",
"phpstan/phpstan": "^1.9",
"phpunit/phpunit": "^9.5",
"psalm/plugin-phpunit": "^0.18.3",
"vimeo/psalm": "^4.11 || ^5.0"
"vimeo/psalm": "^5.0"
},
"type": "library",
"autoload": {
@ -1562,7 +1561,7 @@
],
"support": {
"issues": "https://github.com/doctrine/lexer/issues",
"source": "https://github.com/doctrine/lexer/tree/2.1.0"
"source": "https://github.com/doctrine/lexer/tree/3.0.0"
},
"funding": [
{
@ -1578,7 +1577,7 @@
"type": "tidelift"
}
],
"time": "2022-12-14T08:49:07+00:00"
"time": "2022-12-15T16:57:16+00:00"
},
{
"name": "dragonmantank/cron-expression",
@ -2168,16 +2167,16 @@
},
{
"name": "google/apiclient-services",
"version": "v0.287.0",
"version": "v0.288.0",
"source": {
"type": "git",
"url": "https://github.com/googleapis/google-api-php-client-services.git",
"reference": "ed58596d34272a5cd0dc2c0595d9a678b9834880"
"reference": "de6cfa1556faf4a23d9ab5844508bb576f44fdd7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/ed58596d34272a5cd0dc2c0595d9a678b9834880",
"reference": "ed58596d34272a5cd0dc2c0595d9a678b9834880",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/de6cfa1556faf4a23d9ab5844508bb576f44fdd7",
"reference": "de6cfa1556faf4a23d9ab5844508bb576f44fdd7",
"shasum": ""
},
"require": {
@ -2206,9 +2205,9 @@
],
"support": {
"issues": "https://github.com/googleapis/google-api-php-client-services/issues",
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.287.0"
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.288.0"
},
"time": "2023-02-12T01:08:11+00:00"
"time": "2023-02-18T01:16:11+00:00"
},
{
"name": "google/auth",
@ -4232,16 +4231,16 @@
},
{
"name": "league/commonmark",
"version": "2.3.8",
"version": "2.3.9",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
"reference": "c493585c130544c4e91d2e0e131e6d35cb0cbc47"
"reference": "c1e114f74e518daca2729ea8c4bf1167038fa4b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/c493585c130544c4e91d2e0e131e6d35cb0cbc47",
"reference": "c493585c130544c4e91d2e0e131e6d35cb0cbc47",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/c1e114f74e518daca2729ea8c4bf1167038fa4b5",
"reference": "c1e114f74e518daca2729ea8c4bf1167038fa4b5",
"shasum": ""
},
"require": {
@ -4334,7 +4333,7 @@
"type": "tidelift"
}
],
"time": "2022-12-10T16:02:17+00:00"
"time": "2023-02-15T14:07:24+00:00"
},
{
"name": "league/config",
@ -4504,16 +4503,16 @@
},
{
"name": "league/flysystem",
"version": "3.12.2",
"version": "3.12.3",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
"reference": "f6377c709d2275ed6feaf63e44be7a7162b0e77f"
"reference": "81e87e74dd5213795c7846d65089712d2dda90ce"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/f6377c709d2275ed6feaf63e44be7a7162b0e77f",
"reference": "f6377c709d2275ed6feaf63e44be7a7162b0e77f",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/81e87e74dd5213795c7846d65089712d2dda90ce",
"reference": "81e87e74dd5213795c7846d65089712d2dda90ce",
"shasum": ""
},
"require": {
@ -4575,7 +4574,7 @@
],
"support": {
"issues": "https://github.com/thephpleague/flysystem/issues",
"source": "https://github.com/thephpleague/flysystem/tree/3.12.2"
"source": "https://github.com/thephpleague/flysystem/tree/3.12.3"
},
"funding": [
{
@ -4591,7 +4590,7 @@
"type": "tidelift"
}
],
"time": "2023-01-19T12:02:19+00:00"
"time": "2023-02-18T15:32:41+00:00"
},
{
"name": "league/flysystem-aws-s3-v3",
@ -4928,16 +4927,16 @@
},
{
"name": "livewire/livewire",
"version": "v2.11.2",
"version": "v2.11.3",
"source": {
"type": "git",
"url": "https://github.com/livewire/livewire.git",
"reference": "e92233f31dc1b88fb1ac242b32afe1ab7525ee05"
"reference": "5ab6266a12c637f645d38a2f0f61f84182f6249d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/livewire/livewire/zipball/e92233f31dc1b88fb1ac242b32afe1ab7525ee05",
"reference": "e92233f31dc1b88fb1ac242b32afe1ab7525ee05",
"url": "https://api.github.com/repos/livewire/livewire/zipball/5ab6266a12c637f645d38a2f0f61f84182f6249d",
"reference": "5ab6266a12c637f645d38a2f0f61f84182f6249d",
"shasum": ""
},
"require": {
@ -4989,7 +4988,7 @@
"description": "A front-end framework for Laravel.",
"support": {
"issues": "https://github.com/livewire/livewire/issues",
"source": "https://github.com/livewire/livewire/tree/v2.11.2"
"source": "https://github.com/livewire/livewire/tree/v2.11.3"
},
"funding": [
{
@ -4997,7 +4996,7 @@
"type": "github"
}
],
"time": "2023-01-29T23:45:02+00:00"
"time": "2023-02-20T03:52:35+00:00"
},
{
"name": "microsoft/microsoft-graph",
@ -13071,25 +13070,25 @@
},
{
"name": "beyondcode/laravel-query-detector",
"version": "1.6.0",
"version": "1.7.0",
"source": {
"type": "git",
"url": "https://github.com/beyondcode/laravel-query-detector.git",
"reference": "8261d80c71c97e994c1021fe5c3bd2a1c27106fc"
"reference": "40c7e168fcf7eeb80d8e96f7922e05ab194269c8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/beyondcode/laravel-query-detector/zipball/8261d80c71c97e994c1021fe5c3bd2a1c27106fc",
"reference": "8261d80c71c97e994c1021fe5c3bd2a1c27106fc",
"url": "https://api.github.com/repos/beyondcode/laravel-query-detector/zipball/40c7e168fcf7eeb80d8e96f7922e05ab194269c8",
"reference": "40c7e168fcf7eeb80d8e96f7922e05ab194269c8",
"shasum": ""
},
"require": {
"illuminate/support": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0",
"illuminate/support": "^5.5 || ^6.0 || ^7.0 || ^8.0 || ^9.0|^10.0",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"laravel/legacy-factories": "^1.0",
"orchestra/testbench": "^3.0 || ^4.0 || ^5.0 || ^6.0",
"orchestra/testbench": "^3.0 || ^4.0 || ^5.0 || ^6.0|^8.0",
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
},
"type": "library",
@ -13125,9 +13124,9 @@
],
"support": {
"issues": "https://github.com/beyondcode/laravel-query-detector/issues",
"source": "https://github.com/beyondcode/laravel-query-detector/tree/1.6.0"
"source": "https://github.com/beyondcode/laravel-query-detector/tree/1.7.0"
},
"time": "2022-02-12T16:23:40+00:00"
"time": "2023-02-15T10:37:22+00:00"
},
{
"name": "brianium/paratest",
@ -13703,30 +13702,30 @@
},
{
"name": "doctrine/annotations",
"version": "1.14.3",
"version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/annotations.git",
"reference": "fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af"
"reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/annotations/zipball/fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af",
"reference": "fb0d71a7393298a7b232cbf4c8b1f73f3ec3d5af",
"url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f",
"reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f",
"shasum": ""
},
"require": {
"doctrine/lexer": "^1 || ^2",
"doctrine/lexer": "^2 || ^3",
"ext-tokenizer": "*",
"php": "^7.1 || ^8.0",
"php": "^7.2 || ^8.0",
"psr/cache": "^1 || ^2 || ^3"
},
"require-dev": {
"doctrine/cache": "^1.11 || ^2.0",
"doctrine/coding-standard": "^9 || ^10",
"phpstan/phpstan": "~1.4.10 || ^1.8.0",
"doctrine/cache": "^2.0",
"doctrine/coding-standard": "^10",
"phpstan/phpstan": "^1.8.0",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"symfony/cache": "^4.4 || ^5.4 || ^6",
"symfony/cache": "^5.4 || ^6",
"vimeo/psalm": "^4.10"
},
"suggest": {
@ -13773,9 +13772,9 @@
],
"support": {
"issues": "https://github.com/doctrine/annotations/issues",
"source": "https://github.com/doctrine/annotations/tree/1.14.3"
"source": "https://github.com/doctrine/annotations/tree/2.0.1"
},
"time": "2023-02-01T09:20:38+00:00"
"time": "2023-02-02T22:02:53+00:00"
},
{
"name": "doctrine/instantiator",
@ -14082,27 +14081,27 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.14.2",
"version": "v3.14.4",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "14f0541651841b63640e7aafad041ad55dc7aa88"
"reference": "1b3d9dba63d93b8a202c31e824748218781eae6b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/14f0541651841b63640e7aafad041ad55dc7aa88",
"reference": "14f0541651841b63640e7aafad041ad55dc7aa88",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/1b3d9dba63d93b8a202c31e824748218781eae6b",
"reference": "1b3d9dba63d93b8a202c31e824748218781eae6b",
"shasum": ""
},
"require": {
"composer/semver": "^3.3",
"composer/xdebug-handler": "^3.0.3",
"doctrine/annotations": "^1.14.2 || ^2",
"doctrine/lexer": "^2",
"doctrine/annotations": "^2",
"doctrine/lexer": "^2 || ^3",
"ext-json": "*",
"ext-tokenizer": "*",
"php": "^7.4 || ^8.0",
"sebastian/diff": "^4.0",
"sebastian/diff": "^4.0 || ^5.0",
"symfony/console": "^5.4 || ^6.0",
"symfony/event-dispatcher": "^5.4 || ^6.0",
"symfony/filesystem": "^5.4 || ^6.0",
@ -14160,7 +14159,7 @@
"description": "A tool to automatically fix PHP code style",
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.14.2"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.14.4"
},
"funding": [
{
@ -14168,7 +14167,7 @@
"type": "github"
}
],
"time": "2023-01-29T23:47:01+00:00"
"time": "2023-02-09T21:49:13+00:00"
},
{
"name": "hamcrest/hamcrest-php",
@ -14223,24 +14222,24 @@
},
{
"name": "laracasts/cypress",
"version": "3.0.0",
"version": "3.0.1",
"source": {
"type": "git",
"url": "https://github.com/laracasts/cypress.git",
"reference": "9a9e5d25a51d2cbb410393e6a0d9883aa3304bf5"
"reference": "dd4e61188d4edaf65ffa18851a5df38d0fa0619a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laracasts/cypress/zipball/9a9e5d25a51d2cbb410393e6a0d9883aa3304bf5",
"reference": "9a9e5d25a51d2cbb410393e6a0d9883aa3304bf5",
"url": "https://api.github.com/repos/laracasts/cypress/zipball/dd4e61188d4edaf65ffa18851a5df38d0fa0619a",
"reference": "dd4e61188d4edaf65ffa18851a5df38d0fa0619a",
"shasum": ""
},
"require": {
"illuminate/support": "^6.0|^7.0|^8.0|^9.0",
"illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0",
"php": "^8.0"
},
"require-dev": {
"orchestra/testbench": "^6.0|^7.0",
"orchestra/testbench": "^6.0|^7.0|^8.0",
"phpunit/phpunit": "^8.0|^9.5.10",
"spatie/laravel-ray": "^1.29"
},
@ -14276,9 +14275,9 @@
],
"support": {
"issues": "https://github.com/laracasts/cypress/issues",
"source": "https://github.com/laracasts/cypress/tree/3.0.0"
"source": "https://github.com/laracasts/cypress/tree/3.0.1"
},
"time": "2022-06-27T13:49:35+00:00"
"time": "2023-02-16T20:00:16+00:00"
},
{
"name": "laravel/dusk",
@ -15086,16 +15085,16 @@
},
{
"name": "phpstan/phpstan",
"version": "1.9.17",
"version": "1.9.18",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "204e459e7822f2c586463029f5ecec31bb45a1f2"
"reference": "f2d5cf71be91172a57c649770b73c20ebcffb0bf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/204e459e7822f2c586463029f5ecec31bb45a1f2",
"reference": "204e459e7822f2c586463029f5ecec31bb45a1f2",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/f2d5cf71be91172a57c649770b73c20ebcffb0bf",
"reference": "f2d5cf71be91172a57c649770b73c20ebcffb0bf",
"shasum": ""
},
"require": {
@ -15125,7 +15124,7 @@
],
"support": {
"issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.9.17"
"source": "https://github.com/phpstan/phpstan/tree/1.9.18"
},
"funding": [
{
@ -15141,7 +15140,7 @@
"type": "tidelift"
}
],
"time": "2023-02-08T12:25:00+00:00"
"time": "2023-02-17T15:01:27+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -17310,30 +17309,28 @@
},
{
"name": "zircote/swagger-php",
"version": "3.3.7",
"version": "3.1.0",
"source": {
"type": "git",
"url": "https://github.com/zircote/swagger-php.git",
"reference": "e8c3bb316e385e93a0c7e8b2aa0681079244c381"
"reference": "9d172471e56433b5c7061006b9a766f262a3edfd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/e8c3bb316e385e93a0c7e8b2aa0681079244c381",
"reference": "e8c3bb316e385e93a0c7e8b2aa0681079244c381",
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/9d172471e56433b5c7061006b9a766f262a3edfd",
"reference": "9d172471e56433b5c7061006b9a766f262a3edfd",
"shasum": ""
},
"require": {
"doctrine/annotations": "^1.7",
"doctrine/annotations": "*",
"ext-json": "*",
"php": ">=7.2",
"psr/log": "^1.1 || ^2.0 || ^3.0",
"symfony/finder": ">=2.2",
"symfony/yaml": ">=3.3"
},
"require-dev": {
"composer/package-versions-deprecated": "1.11.99.2",
"friendsofphp/php-cs-fixer": "^2.17 || ^3.0",
"phpunit/phpunit": ">=8.5.14"
"friendsofphp/php-cs-fixer": "^2.16",
"phpunit/phpunit": ">=8"
},
"bin": [
"bin/openapi"
@ -17377,9 +17374,9 @@
],
"support": {
"issues": "https://github.com/zircote/swagger-php/issues",
"source": "https://github.com/zircote/swagger-php/tree/3.3.7"
"source": "https://github.com/zircote/swagger-php/tree/3.1.0"
},
"time": "2023-01-03T21:17:10+00:00"
"time": "2020-09-03T20:18:43+00:00"
}
],
"aliases": [],

View File

@ -201,7 +201,6 @@ return [
App\Providers\MultiDBProvider::class,
App\Providers\ClientPortalServiceProvider::class,
App\Providers\NinjaTranslationServiceProvider::class,
// App\Providers\MailCssInlinerServiceProvider::class,
],
/*

View File

@ -1,18 +0,0 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Css Files
|--------------------------------------------------------------------------
|
| Css file of your style for your emails
| The content of these files will be added directly into the inliner
| Use absolute paths, ie. public_path('css/main.css')
|
*/
'css-files' => [],
];

View File

@ -16,6 +16,7 @@ use App\Models\CompanyGateway;
use App\Models\GatewayType;
use App\Models\Invoice;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\MockAccountData;
use Tests\TestCase;
@ -26,7 +27,8 @@ use Tests\TestCase;
class CompanyGatewayTest extends TestCase
{
use MockAccountData;
use DatabaseTransactions;
// use DatabaseTransactions;
use RefreshDatabase;
protected function setUp() :void
{

View File

@ -10,14 +10,17 @@
*/
namespace Tests\Feature;
use App\DataMapper\CompanySettings;
use Tests\TestCase;
use App\Models\Company;
use App\Models\Activity;
use Tests\MockAccountData;
use App\Utils\Traits\MakesHash;
use App\DataMapper\CompanySettings;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Session;
use Illuminate\Validation\ValidationException;
use Tests\MockAccountData;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\DatabaseTransactions;
/**
* @test
@ -26,8 +29,9 @@ use Tests\TestCase;
class CompanySettingsTest extends TestCase
{
use MakesHash;
use DatabaseTransactions;
// use DatabaseTransactions;
use MockAccountData;
use RefreshDatabase;
public function setUp() :void
{
@ -179,6 +183,7 @@ class CompanySettingsTest extends TestCase
public function testCompanyNullValueMatrixPOST()
{
$settings = CompanySettings::defaults();
$settings->reset_counter_date = null;

View File

@ -217,8 +217,8 @@ trait MockAccountData
$fake_email = $this->faker->email();
$this->account = Account::factory()->create([
'hosted_client_count' => 1000,
'hosted_company_count' => 1000,
'hosted_client_count' => 1000000,
'hosted_company_count' => 1000000,
]);
$this->account->num_users = 3;

View File

@ -75,6 +75,51 @@ class PermissionsTest extends TestCase
$company_token->save();
}
public function testHasExcludedPermissions()
{
$low_cu = CompanyUser::where(['company_id' => $this->company->id, 'user_id' => $this->user->id])->first();
$low_cu->permissions = '["view_client"]';
$low_cu->save();
$this->assertTrue($this->user->hasExcludedPermissions(["view_client"]));
$low_cu = CompanyUser::where(['company_id' => $this->company->id, 'user_id' => $this->user->id])->first();
$low_cu->permissions = 'view_client';
$low_cu->save();
$this->assertTrue($this->user->hasExcludedPermissions(["view_client"]));
}
public function testHasExcludedPermissions2()
{
$low_cu = CompanyUser::where(['company_id' => $this->company->id, 'user_id' => $this->user->id])->first();
$low_cu->permissions = '["view_client","edit_all"]';
$low_cu->save();
$this->assertFalse($this->user->hasExcludedPermissions(["view_client"], ['edit_all']));
$low_cu = CompanyUser::where(['company_id' => $this->company->id, 'user_id' => $this->user->id])->first();
$low_cu->permissions = 'view_client,edit_all';
$low_cu->save();
$this->assertFalse($this->user->hasExcludedPermissions(["view_client"], ['edit_all']));
$low_cu = CompanyUser::where(['company_id' => $this->company->id, 'user_id' => $this->user->id])->first();
$low_cu->permissions = 'view_client,view_all';
$low_cu->save();
$this->assertFalse($this->user->hasExcludedPermissions(["view_client"], ['view_all']));
$low_cu = CompanyUser::where(['company_id' => $this->company->id, 'user_id' => $this->user->id])->first();
$low_cu->permissions = 'view_client,view_invoice';
$low_cu->save();
$this->assertFalse($this->user->hasExcludedPermissions(["view_client"], ['view_invoice']));
}
public function testIntersectPermissions()
{
$low_cu = CompanyUser::where(['company_id' => $this->company->id, 'user_id' => $this->user->id])->first();
@ -212,8 +257,8 @@ class PermissionsTest extends TestCase
public function testExactPermissions()
{
$this->assertTrue($this->user->hasExactPermission("view_client"));
$this->assertFalse($this->user->hasExactPermission("view_all"));
$this->assertTrue($this->user->hasExactPermissionAndAll("view_client"));
$this->assertFalse($this->user->hasExactPermissionAndAll("view_all"));
}
public function testMissingPermissions()
@ -222,8 +267,8 @@ class PermissionsTest extends TestCase
$low_cu->permissions = '[""]';
$low_cu->save();
$this->assertFalse($this->user->hasExactPermission("view_client"));
$this->assertFalse($this->user->hasExactPermission("view_all"));
$this->assertFalse($this->user->hasExactPermissionAndAll("view_client"));
$this->assertFalse($this->user->hasExactPermissionAndAll("view_all"));
}
public function testViewAllValidPermissions()
@ -232,8 +277,8 @@ class PermissionsTest extends TestCase
$low_cu->permissions = '["view_all"]';
$low_cu->save();
$this->assertTrue($this->user->hasExactPermission("view_client"));
$this->assertTrue($this->user->hasExactPermission("view_all"));
$this->assertTrue($this->user->hasExactPermissionAndAll("view_client"));
$this->assertTrue($this->user->hasExactPermissionAndAll("view_all"));
}
public function testReturnTypesOfStripos()