mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-08 13:54:30 -04:00
commit
1b19b3d0a9
@ -1 +1 @@
|
||||
5.1.6
|
||||
5.1.7
|
63
app/Http/Controllers/TwoFactorController.php
Normal file
63
app/Http/Controllers/TwoFactorController.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use PragmaRX\Google2FA\Google2FA;
|
||||
use Crypt;
|
||||
|
||||
class TwoFactorController extends BaseController
|
||||
{
|
||||
public function setupTwoFactor()
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
if ($user->google_2fa_secret)
|
||||
return response()->json(['message' => '2FA already enabled'], 400);
|
||||
elseif(! $user->phone)
|
||||
return response()->json(['message' => ctrans('texts.set_phone_for_two_factor')], 400);
|
||||
elseif(! $user->confirmed)
|
||||
return response()->json(['message' => 'Please confirm your account first'], 400);
|
||||
|
||||
$google2fa = new Google2FA();
|
||||
$secret = $google2fa->generateSecretKey();
|
||||
|
||||
$qr_code = $google2fa->getQRCodeGoogleUrl(
|
||||
config('ninja.app_name')
|
||||
$user->email,
|
||||
$secret
|
||||
);
|
||||
|
||||
$data = [
|
||||
'secret' => $secret,
|
||||
'qrCode' => $qrCode,
|
||||
];
|
||||
|
||||
return response()->json(['data' => $data], 200);
|
||||
|
||||
}
|
||||
|
||||
public function enableTwoFactor()
|
||||
{
|
||||
$user = auth()->user();
|
||||
$secret = request()->input('secret');
|
||||
$oneTimePassword = request()->input('one_time_password');
|
||||
|
||||
if (! $secret || ! \Google2FA::verifyKey($secret, $oneTimePassword)) {
|
||||
return response()->json('message' > ctrans('texts.invalid_one_time_password'));
|
||||
} elseif (! $user->google_2fa_secret && $user->phone && $user->confirmed) {
|
||||
$user->google_2fa_secret = encrypt($secret);
|
||||
$user->save();
|
||||
}
|
||||
|
||||
return response()->json(['message' => ctrans('texts.enabled_two_factor')], 200);
|
||||
}
|
||||
}
|
@ -23,11 +23,16 @@ use App\Http\Requests\User\CreateUserRequest;
|
||||
use App\Http\Requests\User\DestroyUserRequest;
|
||||
use App\Http\Requests\User\DetachCompanyUserRequest;
|
||||
use App\Http\Requests\User\EditUserRequest;
|
||||
use App\Http\Requests\User\ReconfirmUserRequest;
|
||||
use App\Http\Requests\User\ShowUserRequest;
|
||||
use App\Http\Requests\User\StoreUserRequest;
|
||||
use App\Http\Requests\User\UpdateUserRequest;
|
||||
use App\Jobs\Company\CreateCompanyToken;
|
||||
use App\Jobs\Mail\NinjaMailer;
|
||||
use App\Jobs\Mail\NinjaMailerJob;
|
||||
use App\Jobs\Mail\NinjaMailerObject;
|
||||
use App\Jobs\User\UserEmailChanged;
|
||||
use App\Mail\Admin\VerifyUserObject;
|
||||
use App\Models\CompanyUser;
|
||||
use App\Models\User;
|
||||
use App\Repositories\UserRepository;
|
||||
@ -378,11 +383,12 @@ class UserController extends BaseController
|
||||
$new_user = $this->user_repo->save($request->all(), $user);
|
||||
$new_user = $user->fresh();
|
||||
|
||||
|
||||
nlog($old_user);
|
||||
|
||||
if ($old_user_email != $new_email)
|
||||
/* When changing email address we store the former email in case we need to rollback */
|
||||
if ($old_user_email != $new_email) {
|
||||
$user->last_confirmed_email_address = $old_user_email;
|
||||
$user->save();
|
||||
UserEmailChanged::dispatch($new_user, json_decode($old_user), auth()->user()->company());
|
||||
}
|
||||
|
||||
|
||||
if(
|
||||
@ -684,4 +690,70 @@ class UserController extends BaseController
|
||||
|
||||
return response()->json(['message' => ctrans('texts.user_detached')], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach an existing user to a company.
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/users/{user}/reconfirm",
|
||||
* operationId="reconfirmUser",
|
||||
* tags={"users"},
|
||||
* summary="Reconfirm an existing user to a company",
|
||||
* description="Reconfirm an existing user from a company",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Parameter(
|
||||
* name="user",
|
||||
* in="path",
|
||||
* description="The user hashed_id",
|
||||
* example="FD767dfd7",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Success response",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
* @param ReconfirmUserRequest $request
|
||||
* @param User $user
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function reconfirm(ReconfirmUserRequest $request, User $user)
|
||||
{
|
||||
$user->confirmation_code = $this->createDbHash($user->company()->db);
|
||||
$user->save();
|
||||
|
||||
$nmo = new NinjaMailerObject;
|
||||
$nmo->mailable = new NinjaMailer((new VerifyUserObject($user, $user->company()))->build());
|
||||
$nmo->company = $user->company();
|
||||
$nmo->to_user = $user;
|
||||
$nmo->settings = $user->company->settings;
|
||||
|
||||
NinjaMailerJob::dispatch($nmo);
|
||||
|
||||
return response()->json(['message' => ctrans('texts.confirmation_resent')], 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
28
app/Http/Requests/User/ReconfirmUserRequest.php
Normal file
28
app/Http/Requests/User/ReconfirmUserRequest.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\User;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\User;
|
||||
|
||||
class ReconfirmUserRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->Admin();
|
||||
}
|
||||
}
|
@ -54,6 +54,7 @@ use Symfony\Component\HttpFoundation\ParameterBag;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class CSVImport implements ShouldQueue {
|
||||
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, CleanLineItems;
|
||||
|
||||
public $invoice;
|
||||
|
@ -50,21 +50,5 @@ class BouncedEmail extends Mailable
|
||||
->text()
|
||||
->subject($subject);
|
||||
|
||||
//todo
|
||||
/*
|
||||
|
||||
|
||||
//todo determine WHO is notified!! In this instance the _user_ is notified
|
||||
|
||||
Mail::to($invitation->user->email)
|
||||
//->cc('')
|
||||
//->bcc('')
|
||||
->queue(new BouncedEmail($invitation));
|
||||
|
||||
return $this->from('x@gmail.com') //todo
|
||||
->subject(ctrans('texts.confirmation_subject'))
|
||||
->markdown('email.auth.verify', ['user' => $this->user])
|
||||
->text('email.auth.verify_text');
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail\Invoices;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class InvoiceWasPaid extends Mailable
|
||||
{
|
||||
// use Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return $this->from(config('mail.from.address'), config('mail.from.name'))->view('email.invoices.paid');
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail\Quote;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class QuoteWasApproved extends Mailable
|
||||
{
|
||||
// use Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return $this->from(config('mail.from.address'), config('mail.from.name'))->view('email.quotes.approved');
|
||||
}
|
||||
}
|
@ -27,6 +27,8 @@ class TemplateEmail extends Mailable
|
||||
|
||||
private $contact;
|
||||
|
||||
private $company;
|
||||
|
||||
public function __construct($build_email, ClientContact $contact)
|
||||
{
|
||||
$this->build_email = $build_email;
|
||||
@ -34,6 +36,8 @@ class TemplateEmail extends Mailable
|
||||
$this->contact = $contact;
|
||||
|
||||
$this->client = $contact->client;
|
||||
|
||||
$this->company = $contact->company;
|
||||
}
|
||||
|
||||
public function build()
|
||||
@ -44,15 +48,13 @@ class TemplateEmail extends Mailable
|
||||
|
||||
$company = $this->client->company;
|
||||
|
||||
$this->from(config('mail.from.address'), config('mail.from.name'));
|
||||
$this->from(config('mail.from.address'), $this->company->present()->name());
|
||||
|
||||
if (strlen($settings->reply_to_email) > 1) {
|
||||
if (strlen($settings->reply_to_email) > 1)
|
||||
$this->replyTo($settings->reply_to_email, $settings->reply_to_email);
|
||||
}
|
||||
|
||||
if (strlen($settings->bcc_email) > 1) {
|
||||
if (strlen($settings->bcc_email) > 1)
|
||||
$this->bcc($settings->bcc_email, $settings->bcc_email);
|
||||
}
|
||||
|
||||
$this->subject($this->build_email->getSubject())
|
||||
->text('email.template.plain', [
|
||||
|
@ -58,6 +58,7 @@ class UserTransformer extends EntityTransformer
|
||||
'custom_value3' => $user->custom_value3 ?: '',
|
||||
'custom_value4' => $user->custom_value4 ?: '',
|
||||
'oauth_provider_id' => (string) $user->oauth_provider_id,
|
||||
'last_confirmed_email_address' => (string) $user->last_confirmed_email_address ?: '',
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ class Helpers
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function formatCustomFieldValue($custom_fields = null, $field, $value, Client $client = null): ?string
|
||||
public function formatCustomFieldValue($custom_fields, $field, $value, Client $client = null): ?string
|
||||
{
|
||||
$custom_field = '';
|
||||
|
||||
@ -84,7 +84,7 @@ class Helpers
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function makeCustomField($custom_fields = null, $field): string
|
||||
public function makeCustomField($custom_fields, $field): string
|
||||
{
|
||||
if ($custom_fields && property_exists($custom_fields, $field)) {
|
||||
$custom_field = $custom_fields->{$field};
|
||||
|
@ -217,13 +217,17 @@ trait CompanySettingsSaver
|
||||
switch ($key) {
|
||||
case 'int':
|
||||
case 'integer':
|
||||
return ctype_digit(strval(abs($value)));
|
||||
if(is_string($value))
|
||||
return false;
|
||||
|
||||
return is_int($value) || ctype_digit(strval(abs($value)));
|
||||
case 'real':
|
||||
case 'float':
|
||||
case 'double':
|
||||
return is_float($value) || is_numeric(strval($value));
|
||||
case 'string':
|
||||
return method_exists($value, '__toString') || is_null($value) || is_string($value);
|
||||
// return method_exists($value, '__toString') || is_null($value) || is_string($value);
|
||||
return is_null($value) || is_string($value);
|
||||
case 'bool':
|
||||
case 'boolean':
|
||||
return is_bool($value) || (int) filter_var($value, FILTER_VALIDATE_BOOLEAN);
|
||||
|
@ -30,8 +30,8 @@
|
||||
"ext-dom": "*",
|
||||
"ext-json": "*",
|
||||
"ext-libxml": "*",
|
||||
"asgrim/ofxparser": "^1.2",
|
||||
"authorizenet/authorizenet": "^2.0",
|
||||
"bacon/bacon-qr-code": "^2.0",
|
||||
"beganovich/snappdf": "^1.0",
|
||||
"checkout/checkout-sdk-php": "^1.0",
|
||||
"cleverit/ubl_invoice": "^1.3",
|
||||
@ -42,7 +42,7 @@
|
||||
"fzaninotto/faker": "^1.4",
|
||||
"google/apiclient": "^2.7",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"hashids/hashids": "^3.0",
|
||||
"hashids/hashids": "^4.0",
|
||||
"intervention/image": "^2.5",
|
||||
"laracasts/presenter": "^0.2.1",
|
||||
"laravel/framework": "^8.0",
|
||||
@ -59,11 +59,12 @@
|
||||
"maennchen/zipstream-php": "^1.2",
|
||||
"nwidart/laravel-modules": "^8.0",
|
||||
"omnipay/paypal": "^3.0",
|
||||
"pragmarx/google2fa": "^8.0",
|
||||
"predis/predis": "^1.1",
|
||||
"sentry/sentry-laravel": "^2",
|
||||
"stripe/stripe-php": "^7.50",
|
||||
"turbo124/beacon": "^1",
|
||||
"turbo124/laravel-gmail": "^5.0",
|
||||
"dacastro4/laravel-gmail": "dev-master",
|
||||
"webpatser/laravel-countries": "dev-master#75992ad",
|
||||
"wildbit/swiftmailer-postmark": "^3.3"
|
||||
},
|
||||
@ -89,7 +90,6 @@
|
||||
"Database\\Seeders\\": "database/seeders/"
|
||||
},
|
||||
"files": [
|
||||
"app/Libraries/OFX.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
|
1024
composer.lock
generated
1024
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,7 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', ''),
|
||||
'app_version' => '5.1.6',
|
||||
'app_version' => '5.1.7',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', false),
|
||||
|
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class EmailLastConfirmedEmailAddressUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function(Blueprint $table){
|
||||
$table->string('last_confirmed_email_address')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@ -146,6 +146,9 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
|
||||
Route::resource('tokens', 'TokenController')->middleware('password_protected'); // name = (tokens. index / create / show / update / destroy / edit
|
||||
Route::post('tokens/bulk', 'TokenController@bulk')->name('tokens.bulk')->middleware('password_protected');
|
||||
|
||||
Route::get('settings/enable_two_factor', 'TwoFactorController@setupTwoFactor');
|
||||
Route::post('settings/enable_two_factor', 'TwoFactorController@enableTwoFactor');
|
||||
|
||||
Route::resource('vendors', 'VendorController'); // name = (vendors. index / create / show / update / destroy / edit
|
||||
Route::post('vendors/bulk', 'VendorController@bulk')->name('vendors.bulk');
|
||||
Route::put('vendors/{vendor}/upload', 'VendorController@upload');
|
||||
@ -156,6 +159,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
|
||||
Route::post('users/{user}/attach_to_company', 'UserController@attach')->middleware('password_protected');
|
||||
Route::delete('users/{user}/detach_from_company', 'UserController@detach')->middleware('password_protected');
|
||||
Route::post('users/bulk', 'UserController@bulk')->name('users.bulk')->middleware('password_protected');
|
||||
Route::post('/user/{user}/reconfirm', 'UserController@reconfirm')->middleware('password_protected');
|
||||
|
||||
Route::resource('webhooks', 'WebhookController');
|
||||
Route::post('webhooks/bulk', 'WebhookController@bulk')->name('webhooks.bulk');
|
||||
|
Loading…
x
Reference in New Issue
Block a user