mirror of https://github.com/Bubka/2FAuth.git
Compare commits
92 Commits
1455084bed
...
2e8a9f75b5
Author | SHA1 | Date |
---|---|---|
Bubka | 2e8a9f75b5 | |
Bubka | dd7d3d95df | |
Bubka | 2e296ba3c1 | |
Bubka | 2db5adfe3b | |
Bubka | 9c8d349e1c | |
Bubka | 2bfd4850ff | |
Bubka | 4516e8d33a | |
Bubka | 80d43911dc | |
Bubka | 790cbdcf28 | |
Bubka | f2c9f8aaa8 | |
Bubka | f4624e2793 | |
Bubka | 1ef4370002 | |
Bubka | c0d4f58680 | |
Bubka | 1ac95d8439 | |
Bubka | 2002e29641 | |
Bubka | 999fc1bf5e | |
Bubka | aaf06d8cf1 | |
Bubka | 704166cfd6 | |
Bubka | b56ee8b04f | |
Bubka | 86e7601328 | |
Bubka | e956959f69 | |
Bubka | 480268a606 | |
Bubka | e6d201d882 | |
Bubka | 1a26c75325 | |
Bubka | edd810cafd | |
Bubka | fb029f77f6 | |
Bubka | 9dddf1c14c | |
Bubka | 70f884270e | |
Bubka | 9519d5838c | |
Bubka | 214c1c2349 | |
Bubka | 1c51541d4d | |
Bubka | b280636296 | |
Bubka | 4491852568 | |
Bubka | 4ef3efd6ce | |
Bubka | 3eed7c8f5b | |
Bubka | fd5520c1cf | |
Bubka | 88d37394d3 | |
Bubka | 04078b09aa | |
Bubka | 1e42008be7 | |
Bubka | a6646b2e9d | |
Bubka | bfa1cc99c3 | |
Bubka | 21fa77f348 | |
Bubka | ee4b21eab2 | |
Bubka | a5b722c560 | |
Bubka | 5d01685ae1 | |
Bubka | f591910719 | |
Bubka | 699a2a1f9e | |
Bubka | 444b110c05 | |
Bubka | f398768f4e | |
Bubka | 6aeb7dcbc9 | |
Bubka | 91242860f0 | |
Bubka | 96f883d19a | |
Bubka | 37e4711071 | |
Bubka | fab67097bc | |
Bubka | db3a732b15 | |
Bubka | 3b156df8a2 | |
Bubka | 6fe00585e5 | |
Bubka | 8b397750e8 | |
Bubka | d96c943927 | |
Bubka | 5249b2ab97 | |
Bubka | 342448b352 | |
Bubka | 784b5206b9 | |
Bubka | 12bef04dd9 | |
Bubka | cee032897a | |
Bubka | 903509b0c6 | |
Bubka | 406f0095ea | |
Bubka | 1e3cb32bc2 | |
Bubka | 119eca6c7e | |
Bubka | 300d3c3dc8 | |
Bubka | f2d4c43239 | |
Bubka | 35f2f1df84 | |
Bubka | 5ed8414d2c | |
Bubka | 91ca9f61c0 | |
Bubka | df33bcdbe0 | |
Bubka | 8c1df18204 | |
Bubka | b4e3f97313 | |
Bubka | 45d2ca55ec | |
Bubka | b9abcb3d18 | |
Bubka | 98f711800d | |
Bubka | d2427364a5 | |
Bubka | f4edbcd044 | |
Bubka | 1a9f011809 | |
Bubka | 7593fc91a8 | |
Bubka | 9fa4142a12 | |
Bubka | 78a1eebdd2 | |
Bubka | ddedcef61a | |
Bubka | 308bf3c436 | |
Bubka | 5fd7f43968 | |
Bubka | e3ded669ca | |
Bubka | 6ffa07211e | |
Bubka | 5710ccc9d3 | |
Bubka | 26d245aa79 |
11
.env.example
11
.env.example
|
@ -35,6 +35,13 @@ APP_KEY=
|
|||
APP_URL=http://localhost
|
||||
|
||||
|
||||
# If you want to serve js assets from a CDN (like https://cdn.example.com),
|
||||
# uncomment the following line and set this var with the CDN url.
|
||||
# Otherwise, let this line commented.
|
||||
|
||||
# ASSET_URL=http://localhost
|
||||
|
||||
|
||||
# The domain subdirectory from which you want to serve 2FAuth.
|
||||
# This must reflect the path targeted by APP_URL.
|
||||
#
|
||||
|
@ -54,8 +61,8 @@ IS_DEMO_APP=false
|
|||
|
||||
# The log channel defines where your log entries go to.
|
||||
# 'daily' is the default logging mode giving you 7 daily rotated log files in /storage/logs/.
|
||||
# Several other options exist. You can use 'single' for one big fat error log (not recommended).
|
||||
# Also available are 'syslog', 'errorlog' and 'stdout' which will log to the system itself.
|
||||
# Also available are 'errorlog', 'syslog', 'stderr', 'papertrail', 'slack' and a 'stack' channel
|
||||
# to combine multiple channels into a single one.
|
||||
|
||||
LOG_CHANNEL=daily
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ COPY . .
|
|||
RUN mv .env.testing .env
|
||||
RUN composer install
|
||||
RUN php artisan key:generate
|
||||
COPY docker/php-test.ini /usr/local/etc/php/php.ini
|
||||
ENTRYPOINT [ "/srv/vendor/bin/phpunit" ]
|
||||
|
||||
FROM alpine:${ALPINE_VERSION}
|
||||
|
@ -124,13 +125,18 @@ ENV \
|
|||
# This variable must match your installation's external address.
|
||||
# Webauthn won't work otherwise.
|
||||
APP_URL=http://localhost \
|
||||
# If you want to serve js assets from a CDN (like https://cdn.example.com),
|
||||
# uncomment the following line and set this var with the CDN url.
|
||||
# Otherwise, let this line commented.
|
||||
# ASSET_URL=http://localhost \
|
||||
#
|
||||
# Turn this to true if you want your app to react like a demo.
|
||||
# The Demo mode reset the app content every hours and set a generic demo user.
|
||||
IS_DEMO_APP=false \
|
||||
# The log channel defines where your log entries go to.
|
||||
# 'daily' is the default logging mode giving you 5 daily rotated log files in /storage/logs/.
|
||||
# Several other options exist. You can use 'single' for one big fat error log (not recommended).
|
||||
# Also available are 'syslog', 'errorlog' and 'stdout' which will log to the system itself.
|
||||
# 'daily' is the default logging mode giving you 7 daily rotated log files in /storage/logs/.
|
||||
# Also available are 'errorlog', 'syslog', 'stderr', 'papertrail', 'slack' and a 'stack' channel
|
||||
# to combine multiple channels into a single one.
|
||||
LOG_CHANNEL=daily \
|
||||
# Log level. You can set this from least severe to most severe:
|
||||
# debug, info, notice, warning, error, critical, alert, emergency
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
<?php
|
||||
|
||||
namespace App\Api\v1\Controllers;
|
||||
|
||||
use App\Api\v1\Requests\UserManagerStoreRequest;
|
||||
use App\Api\v1\Requests\UserManagerPromoteRequest;
|
||||
use App\Api\v1\Resources\UserManagerResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Laravel\Passport\TokenRepository;
|
||||
|
||||
class UserManagerController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display all users.
|
||||
*
|
||||
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
return UserManagerResource::collection(User::all());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a user
|
||||
*
|
||||
* @return \App\Api\v1\Resources\UserManagerResource
|
||||
*/
|
||||
public function show(User $user)
|
||||
{
|
||||
return new UserManagerResource($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset user's password
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function resetPassword(Request $request, User $user)
|
||||
{
|
||||
Log::info(sprintf('Password reset for User ID #%s requested by User ID #%s', $user->id, $request->user()->id));
|
||||
|
||||
$credentials = [
|
||||
'token' => $this->broker()->createToken($user),
|
||||
'email' => $user->email,
|
||||
'password' => $user->password,
|
||||
];
|
||||
|
||||
$response = $this->broker()->reset(
|
||||
$credentials, function ($user) {
|
||||
$user->resetPassword();
|
||||
$user->save();
|
||||
}
|
||||
);
|
||||
|
||||
if ($response == Password::PASSWORD_RESET) {
|
||||
Log::info(sprintf('Temporary password set for User ID #%s', $user->id));
|
||||
|
||||
$response = $this->broker()->sendResetLink(
|
||||
['email' => $credentials['email']]
|
||||
);
|
||||
}
|
||||
else {
|
||||
return response()->json([
|
||||
'message' => 'bad request',
|
||||
'reason' => is_string($response) ? __($response) : __('errors.no_pwd_reset_for_this_user_type')
|
||||
], 400);
|
||||
}
|
||||
|
||||
return $response == Password::RESET_LINK_SENT
|
||||
? new UserManagerResource($user)
|
||||
: response()->json([
|
||||
'message' => 'bad request',
|
||||
'reason' => __($response)
|
||||
], 400);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created user in storage.
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function store(UserManagerStoreRequest $request)
|
||||
{
|
||||
$validated = $request->validated();
|
||||
|
||||
$user = User::create([
|
||||
'name' => $validated['name'],
|
||||
'email' => $validated['email'],
|
||||
'password' => Hash::make($validated['password']),
|
||||
]);
|
||||
|
||||
Log::info(sprintf('User ID #%s created by user ID #%s', $user->id, $request->user()->id));
|
||||
|
||||
if ($validated['is_admin']) {
|
||||
$user->promoteToAdministrator();
|
||||
$user->save();
|
||||
Log::notice(sprintf('User ID #%s set as administrator at creation by user ID #%s', $user->id, $request->user()->id));
|
||||
}
|
||||
|
||||
$user->refresh();
|
||||
|
||||
return (new UserManagerResource($user))
|
||||
->response()
|
||||
->setStatusCode(201);
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge user's PATs.
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function revokePATs(Request $request, User $user, TokenRepository $tokenRepository)
|
||||
{
|
||||
Log::info(sprintf('Deletion of all personal access tokens for User ID #%s requested by User ID #%s', $user->id, $request->user()->id));
|
||||
|
||||
$tokens = $tokenRepository->forUser($user->getAuthIdentifier());
|
||||
|
||||
$tokens->load('client')->filter(function ($token) {
|
||||
return $token->client->personal_access_client && ! $token->revoked;
|
||||
})->each(function ($token) {
|
||||
$token->revoke();
|
||||
});
|
||||
|
||||
Log::info(sprintf('All personal access tokens for User ID #%s have been revoked', $user->id));
|
||||
|
||||
return response()->json(null, 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge user's webauthn credentials.
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function revokeWebauthnCredentials(Request $request, User $user)
|
||||
{
|
||||
Log::info(sprintf('Deletion of all security devices for User ID #%s requested by User ID #%s', $user->id, $request->user()->id));
|
||||
|
||||
$user->flushCredentials();
|
||||
|
||||
// WebauthnOnly user options need to be reset to prevent impossible login when
|
||||
// no more registered device exists.
|
||||
// See #110
|
||||
if (blank($user->webAuthnCredentials()->WhereEnabled()->get())) {
|
||||
$user['preferences->useWebauthnOnly'] = false;
|
||||
$user->save();
|
||||
Log::notice(sprintf('No more Webauthn credential for user ID #%s, useWebauthnOnly user preference reset to false', $user->id));
|
||||
}
|
||||
|
||||
Log::info(sprintf('All security devices for User ID #%s have been revoked', $user->id));
|
||||
|
||||
return response()->json(null, 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified user from storage.
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function destroy(Request $request, User $user)
|
||||
{
|
||||
// This will delete the user and all its 2FAs & Groups thanks to the onCascadeDelete constrains.
|
||||
// Deletion will not be done (and returns False) if the user is the only existing admin (see UserObserver clas)
|
||||
return $user->delete() === false
|
||||
? response()->json([
|
||||
'message' => __('errors.cannot_delete_the_only_admin'),
|
||||
], 403)
|
||||
: response()->json(null, 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Promote (or demote) a user
|
||||
*
|
||||
* @return \App\Api\v1\Resources\UserManagerResource
|
||||
*/
|
||||
public function promote(UserManagerPromoteRequest $request, User $user)
|
||||
{
|
||||
$user->promoteToAdministrator($request->validated('is_admin'));
|
||||
$user->save();
|
||||
|
||||
Log::info(sprintf('User ID #%s set is_admin=%s for User ID #%s', $request->user()->id, $user->isAdministrator(), $user->id));
|
||||
|
||||
return new UserManagerResource($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the broker to be used during password reset.
|
||||
*
|
||||
* @return \Illuminate\Contracts\Auth\PasswordBroker|\Illuminate\Auth\Passwords\PasswordBroker
|
||||
*/
|
||||
protected function broker()
|
||||
{
|
||||
return Password::broker();
|
||||
}
|
||||
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Api\v1\Requests;
|
||||
|
||||
use App\Rules\IsValideEmailList;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
|
@ -24,8 +25,16 @@ class SettingUpdateRequest extends FormRequest
|
|||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'value' => 'required',
|
||||
$rule = [
|
||||
'value' => [
|
||||
'required',
|
||||
]
|
||||
];
|
||||
|
||||
if ($this->route()?->parameter('settingName') == 'restrictList') {
|
||||
$rule['value'][] = new IsValideEmailList;
|
||||
}
|
||||
|
||||
return $rule;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Api\v1\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class UserManagerPromoteRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return Auth::user()->isAdministrator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'is_admin' => 'required|boolean',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace App\Api\v1\Requests;
|
||||
|
||||
use App\Http\Requests\UserStoreRequest;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class UserManagerStoreRequest extends UserStoreRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return Auth::user()->isAdministrator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return array_merge(
|
||||
parent::rules(),
|
||||
[
|
||||
'is_admin' => 'required|boolean',
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
namespace App\Api\v1\Resources;
|
||||
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Laravel\Passport\TokenRepository;
|
||||
|
||||
/**
|
||||
* @property mixed $id
|
||||
* @property string $name
|
||||
* @property string $email
|
||||
* @property string $oauth_provider
|
||||
* @property \Illuminate\Support\Collection<array-key, mixed> $preferences
|
||||
* @property string $is_admin
|
||||
* @property string $last_seen_at
|
||||
* @property string $created_at
|
||||
* @property string $updated_at
|
||||
* @property int|null $twofaccounts_count
|
||||
*/
|
||||
class UserManagerResource extends UserResource
|
||||
{
|
||||
/**
|
||||
* The "data" wrapper that should be applied.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public static $wrap = 'info';
|
||||
|
||||
/**
|
||||
* Create a new resource instance.
|
||||
*
|
||||
* @param mixed $resource
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($resource)
|
||||
{
|
||||
$this->resource = $resource;
|
||||
$password_reset = null;
|
||||
|
||||
// Password reset token
|
||||
$resetToken = DB::table(config('auth.passwords.users.table'))->where(
|
||||
'email', $this->resource->getEmailForPasswordReset()
|
||||
)->first();
|
||||
|
||||
if ($resetToken) {
|
||||
$password_reset = $this->tokenExpired($resetToken->created_at)
|
||||
? 0
|
||||
: $resetToken->created_at;
|
||||
}
|
||||
|
||||
// Personal Access Tokens (PATs)
|
||||
$tokenRepository = App::make(TokenRepository::class);
|
||||
$tokens = $tokenRepository->forUser($this->resource->getAuthIdentifier());
|
||||
|
||||
$PATs_count = $tokens->load('client')->filter(function ($token) {
|
||||
return $token->client->personal_access_client && ! $token->revoked;
|
||||
})->count();
|
||||
|
||||
$this->with = [
|
||||
'password_reset' => $password_reset,
|
||||
'valid_personal_access_tokens' => $PATs_count,
|
||||
'webauthn_credentials' => $this->resource->webAuthnCredentials()->count()
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the token has expired.
|
||||
*
|
||||
* @param string $createdAt
|
||||
* @return bool
|
||||
*/
|
||||
protected function tokenExpired($createdAt)
|
||||
{
|
||||
// See Illuminate\Auth\Passwords\DatabaseTokenRepository
|
||||
return Carbon::parse($createdAt)->addSeconds(config('auth.passwords.users.expires', 60)*60)->isPast();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return array_merge(
|
||||
parent::toArray($request),
|
||||
[
|
||||
'twofaccounts_count' => is_null($this->twofaccounts_count) ? 0 : $this->twofaccounts_count,
|
||||
'last_seen_at' => Carbon::parse($this->last_seen_at)->locale(App::getLocale())->diffForHumans(),
|
||||
'created_at' => Carbon::parse($this->created_at)->locale(App::getLocale())->diffForHumans(),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -60,12 +60,12 @@ trait ResetTrait
|
|||
protected function flushDB() : void
|
||||
{
|
||||
// Reset the db
|
||||
DB::table('password_resets')->delete();
|
||||
DB::table(config('auth.passwords.users.table'))->delete();
|
||||
DB::table('oauth_access_tokens')->delete();
|
||||
DB::table('oauth_personal_access_clients')->delete();
|
||||
DB::table('oauth_refresh_tokens')->delete();
|
||||
DB::table('webauthn_credentials')->delete();
|
||||
DB::table('webauthn_recoveries')->delete();
|
||||
DB::table(config('auth.passwords.webauthn.table'))->delete();
|
||||
DB::table('twofaccounts')->delete();
|
||||
DB::table('groups')->delete();
|
||||
DB::table('users')->delete();
|
||||
|
|
|
@ -45,7 +45,8 @@ class Handler extends ExceptionHandler
|
|||
|
||||
$this->renderable(function (InvalidQrCodeException $exception, $request) {
|
||||
return response()->json([
|
||||
'message' => 'not a valid QR code', ], 400);
|
||||
'message' => $exception->getMessage(),
|
||||
], 400);
|
||||
});
|
||||
|
||||
$this->renderable(function (InvalidSecretException $exception, $request) {
|
||||
|
|
|
@ -63,7 +63,7 @@ class RemoteUserProvider implements UserProvider
|
|||
Log::info(sprintf('Remote user %s created with email address %s', var_export($user->name, true), var_export($user->email, true)));
|
||||
|
||||
if (User::count() === 1) {
|
||||
$user->is_admin = true;
|
||||
$user->promoteToAdministrator();
|
||||
$user->save();
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -107,16 +107,21 @@ class LoginController extends Controller
|
|||
{
|
||||
$this->clearLoginAttempts($request);
|
||||
|
||||
$name = $this->guard()->user()?->name;
|
||||
/**
|
||||
* @var \App\Models\User|null
|
||||
*/
|
||||
$user = $this->guard()->user();
|
||||
$name = $user?->name;
|
||||
|
||||
$this->authenticated($request, $this->guard()->user());
|
||||
|
||||
return response()->json([
|
||||
'message' => 'authenticated',
|
||||
'id' => $user->id,
|
||||
'name' => $name,
|
||||
'email' => $this->guard()->user()->email,
|
||||
'preferences' => $this->guard()->user()->preferences,
|
||||
'is_admin' => $this->guard()->user()->is_admin,
|
||||
'email' => $user->email,
|
||||
'preferences' => $user->preferences,
|
||||
'is_admin' => $user->isAdministrator(),
|
||||
], Response::HTTP_OK);
|
||||
}
|
||||
|
||||
|
|
|
@ -43,13 +43,17 @@ class RegisterController extends Controller
|
|||
event(new Registered($user = $this->create($validated)));
|
||||
|
||||
$this->guard()->login($user);
|
||||
/**
|
||||
* @var \App\Models\User|null
|
||||
*/
|
||||
$user = $this->guard()->user();
|
||||
|
||||
return response()->json([
|
||||
'message' => 'account created',
|
||||
'name' => $user->name,
|
||||
'email' => $user->email,
|
||||
'preferences' => $this->guard()->user()->preferences,
|
||||
'is_admin' => $this->guard()->user()->is_admin,
|
||||
'preferences' => $user->preferences,
|
||||
'is_admin' => $user->isAdministrator(),
|
||||
], 201);
|
||||
}
|
||||
|
||||
|
@ -69,7 +73,7 @@ class RegisterController extends Controller
|
|||
Log::info(sprintf('User ID #%s created', $user->id));
|
||||
|
||||
if (User::count() == 1) {
|
||||
$user->is_admin = true;
|
||||
$user->promoteToAdministrator();
|
||||
$user->save();
|
||||
Log::notice(sprintf('User ID #%s set as administrator', $user->id));
|
||||
}
|
||||
|
|
|
@ -56,8 +56,8 @@ class SocialiteController extends Controller
|
|||
if (User::where('email', $socialiteEmail)->exists()) {
|
||||
return redirect('/error?err=sso_email_already_used');
|
||||
} elseif (User::count() === 0) {
|
||||
$user->is_admin = true;
|
||||
} elseif (Settings::get('disableRegistration')) {
|
||||
$user->promoteToAdministrator();
|
||||
} elseif (Settings::get('disableRegistration') && ! Settings::get('keepSsoRegistrationEnabled')) {
|
||||
return redirect('/error?err=sso_no_register');
|
||||
}
|
||||
$user->password = bcrypt(Str::random());
|
||||
|
|
|
@ -24,6 +24,8 @@ class UserController extends Controller
|
|||
$user = $request->user();
|
||||
$validated = $request->validated();
|
||||
|
||||
$this->authorize('update', $user);
|
||||
|
||||
if (config('auth.defaults.guard') === 'reverse-proxy-guard' || $user->oauth_provider) {
|
||||
Log::notice('Account update rejected: reverse-proxy-guard enabled or account from external sso provider');
|
||||
|
||||
|
@ -57,37 +59,16 @@ class UserController extends Controller
|
|||
$validated = $request->validated();
|
||||
$user = Auth::user();
|
||||
|
||||
Log::info(sprintf('Deletion of user ID #%s requested', $user->id));
|
||||
|
||||
if ($user->is_admin && User::admins()->count() == 1) {
|
||||
return response()->json(['message' => __('errors.cannot_delete_the_only_admin')], 400);
|
||||
}
|
||||
|
||||
if (! Hash::check($validated['password'], Auth::user()->password)) {
|
||||
return response()->json(['message' => __('errors.wrong_current_password')], 400);
|
||||
}
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($user) {
|
||||
DB::table('twofaccounts')->where('user_id', $user->id)->delete();
|
||||
DB::table('groups')->where('user_id', $user->id)->delete();
|
||||
DB::table('webauthn_credentials')->where('authenticatable_id', $user->id)->delete();
|
||||
DB::table('webauthn_recoveries')->where('email', $user->email)->delete();
|
||||
DB::table('oauth_access_tokens')->where('user_id', $user->id)->delete();
|
||||
DB::table('password_resets')->where('email', $user->email)->delete();
|
||||
DB::table('users')->where('id', $user->id)->delete();
|
||||
});
|
||||
}
|
||||
// @codeCoverageIgnoreStart
|
||||
catch (\Throwable $e) {
|
||||
Log::error(sprintf('Deletion of user ID #%s failed, transaction has been rolled-back', $user->id));
|
||||
|
||||
return response()->json(['message' => __('errors.user_deletion_failed')], 400);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
Log::info(sprintf('User ID #%s deleted', $user->id));
|
||||
|
||||
return response()->json(null, 204);
|
||||
// This will delete the user and all its 2FAs & Groups thanks to the onCascadeDelete constrains.
|
||||
// Deletion will not be done (and returns False) if the user is the only existing admin (see UserObserver clas)
|
||||
return $user->delete() === false
|
||||
? response()->json([
|
||||
'message' => __('errors.cannot_delete_the_only_admin'),
|
||||
], 400)
|
||||
: response()->json(null, 204);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -147,8 +147,11 @@ class WebAuthnLoginController extends Controller
|
|||
|
||||
return response()->json([
|
||||
'message' => 'authenticated',
|
||||
'id' => $user->id,
|
||||
'name' => $user->name,
|
||||
'email' => $user->email,
|
||||
'preferences' => $user->preferences,
|
||||
'is_admin' => $user->isAdministrator(),
|
||||
], Response::HTTP_OK);
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ class WebAuthnManageController extends Controller
|
|||
if (blank($user->webAuthnCredentials()->WhereEnabled()->get())) {
|
||||
$request->user()->preferences['useWebauthnOnly'] = false;
|
||||
$request->user()->save();
|
||||
Log::notice(sprintf('No more Webauthn credential for user ID #%s, user Webauthn options reset to default', $user->id));
|
||||
Log::notice(sprintf('No more Webauthn credential for user ID #%s, useWebauthnOnly user preference reset to false', $user->id));
|
||||
}
|
||||
|
||||
Log::info(sprintf('User ID #%s revoked a security device', $user->id));
|
||||
|
|
|
@ -50,7 +50,7 @@ class WebAuthnRecoveryController extends Controller
|
|||
if ($this->shouldRevokeAllCredentials($request)) {
|
||||
$user->flushCredentials();
|
||||
}
|
||||
$user->preferences['useWebauthnOnly'] = false;
|
||||
$user['preferences->useWebauthnOnly'] = false;
|
||||
$user->save();
|
||||
Log::notice(sprintf('Legacy login restored for user ID #%s', $user->id));
|
||||
} else {
|
||||
|
|
|
@ -3,10 +3,13 @@
|
|||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Facades\Settings;
|
||||
use App\Notifications\TestEmailSettingNotification;
|
||||
use App\Services\ReleaseRadarService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class SystemController extends Controller
|
||||
{
|
||||
|
@ -34,27 +37,15 @@ class SystemController extends Controller
|
|||
$infos['common']['Operating system'] = PHP_OS;
|
||||
$infos['common']['interface'] = PHP_SAPI;
|
||||
// Auth & Security infos
|
||||
if (! is_null($request->user())) {
|
||||
$infos['common']['Auth guard'] = config('auth.defaults.guard');
|
||||
if ($infos['common']['Auth guard'] === 'reverse-proxy-guard') {
|
||||
$infos['common']['Auth proxy logout url'] = config('2fauth.config.proxyLogoutUrl');
|
||||
$infos['common']['Auth proxy header for user'] = config('auth.auth_proxy_headers.user');
|
||||
$infos['common']['Auth proxy header for email'] = config('auth.auth_proxy_headers.email');
|
||||
}
|
||||
$infos['common']['webauthn user verification'] = config('webauthn.user_verification');
|
||||
$infos['common']['Trusted proxies'] = config('2fauth.config.trustedProxies') ?: 'none';
|
||||
|
||||
// Admin settings
|
||||
if ($request->user()->is_admin == true) {
|
||||
$infos['admin_settings']['useEncryption'] = Settings::get('useEncryption');
|
||||
$infos['admin_settings']['lastRadarScan'] = Carbon::parse(Settings::get('lastRadarScan'))->format('Y-m-d H:i:s');
|
||||
$infos['admin_settings']['checkForUpdate'] = Settings::get('checkForUpdate');
|
||||
}
|
||||
}
|
||||
// User info
|
||||
if ($request->user()) {
|
||||
$infos['user_preferences'] = $request->user()->preferences->toArray();
|
||||
$infos['common']['Auth guard'] = config('auth.defaults.guard');
|
||||
if ($infos['common']['Auth guard'] === 'reverse-proxy-guard') {
|
||||
$infos['common']['Auth proxy logout url'] = config('2fauth.config.proxyLogoutUrl');
|
||||
$infos['common']['Auth proxy header for user'] = config('auth.auth_proxy_headers.user');
|
||||
$infos['common']['Auth proxy header for email'] = config('auth.auth_proxy_headers.email');
|
||||
}
|
||||
$infos['common']['webauthn user verification'] = config('webauthn.user_verification');
|
||||
$infos['common']['Trusted proxies'] = config('2fauth.config.trustedProxies') ?: 'none';
|
||||
$infos['common']['lastRadarScan'] = Carbon::parse(Settings::get('lastRadarScan'))->format('Y-m-d H:i:s');
|
||||
|
||||
return response()->json($infos);
|
||||
}
|
||||
|
@ -70,4 +61,54 @@ class SystemController extends Controller
|
|||
|
||||
return response()->json(['newRelease' => $release]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a test email
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function testEmail(Request $request)
|
||||
{
|
||||
try {
|
||||
$request->user()->notify(new TestEmailSettingNotification());
|
||||
} catch (\Throwable $th) {
|
||||
Log::error($th->getMessage());
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'Ok']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all app caches and rebuild them
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function optimize(Request $request)
|
||||
{
|
||||
$configCode = Artisan::call('config:cache');
|
||||
$routeCode = Artisan::call('route:cache');
|
||||
$eventCode = Artisan::call('event:cache');
|
||||
$viewCode = Artisan::call('view:cache');
|
||||
|
||||
return response()->json([
|
||||
'config-exit-code' => $configCode,
|
||||
'route-exit-code' => $routeCode,
|
||||
'event-exit-code' => $eventCode,
|
||||
'view-exit-code' => $viewCode,
|
||||
], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears application cache
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function clear(Request $request)
|
||||
{
|
||||
$exitCode = Artisan::call('optimize:clear');
|
||||
|
||||
return response()->json(['exit-code' => $exitCode], 200);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -48,8 +48,8 @@ class Kernel extends HttpKernel
|
|||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
\App\Http\Middleware\Authenticate::class,
|
||||
\App\Http\Middleware\LogUserLastSeen::class,
|
||||
\App\Http\Middleware\KickOutInactiveUser::class,
|
||||
\App\Http\Middleware\LogUserLastSeen::class,
|
||||
\App\Http\Middleware\SetLanguage::class,
|
||||
\App\Http\Middleware\CustomCreateFreshApiToken::class,
|
||||
],
|
||||
|
|
|
@ -16,7 +16,7 @@ class AdminOnly
|
|||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if (! Auth::user()->is_admin) {
|
||||
if (! Auth::user()->isAdministrator()) {
|
||||
throw new AuthorizationException;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,11 +38,8 @@ class KickOutInactiveUser
|
|||
if ($kickUserAfterXSecond > 0 && $inactiveFor > $kickUserAfterXSecond) {
|
||||
$user->last_seen_at = $now->format('Y-m-d H:i:s');
|
||||
$user->save();
|
||||
|
||||
Log::info(sprintf('User ID #%s detected as inactive, authentication rejected', $user->id));
|
||||
if (method_exists('Illuminate\Support\Facades\Auth', 'logout')) {
|
||||
Auth::logout();
|
||||
}
|
||||
Auth::guard('web-guard')->logout();
|
||||
|
||||
return response()->json(['message' => 'inactivity detected'], Response::HTTP_I_AM_A_TEAPOT);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Rules\ComplyWithEmailRestrictionPolicy;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UserStoreRequest extends FormRequest
|
||||
|
@ -24,8 +25,15 @@ class UserStoreRequest extends FormRequest
|
|||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'unique:App\Models\User,name|required|string|max:191',
|
||||
'email' => 'unique:App\Models\User,email|required|string|email|max:191',
|
||||
'name' => 'unique:App\Models\User,name|required|string|max:191',
|
||||
'email' => [
|
||||
'unique:App\Models\User,email',
|
||||
'required',
|
||||
'string',
|
||||
'email',
|
||||
'max:191',
|
||||
new ComplyWithEmailRestrictionPolicy,
|
||||
],
|
||||
'password' => 'required|string|min:8|confirmed',
|
||||
];
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Rules\ComplyWithEmailRestrictionPolicy;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
@ -37,6 +38,7 @@ class UserUpdateRequest extends FormRequest
|
|||
'email',
|
||||
'max:191',
|
||||
Rule::unique('users')->ignore($this->user()->id),
|
||||
new ComplyWithEmailRestrictionPolicy,
|
||||
],
|
||||
'password' => 'required',
|
||||
];
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use Illuminate\Notifications\Events\NotificationSent;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class LogNotification
|
||||
{
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(NotificationSent $event)
|
||||
{
|
||||
// $event->channel
|
||||
// $event->notifiable
|
||||
// $event->notification
|
||||
// $event->response
|
||||
|
||||
Log::info(sprintf('Notification of type %s sent via channel %s to user ID #%s', get_class($event->notification), $event->channel, $event->notifiable->id));
|
||||
}
|
||||
}
|
|
@ -52,6 +52,7 @@ class Group extends Model
|
|||
*/
|
||||
protected $casts = [
|
||||
'twofaccounts_count' => 'integer',
|
||||
'user_id' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -145,7 +145,9 @@ class TwoFAccount extends Model implements Sortable
|
|||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [];
|
||||
protected $casts = [
|
||||
'user_id' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* The event map for the model.
|
||||
|
|
|
@ -3,11 +3,14 @@
|
|||
namespace App\Models;
|
||||
|
||||
use App\Models\Traits\WebAuthnManageCredentials;
|
||||
use Illuminate\Auth\Events\PasswordReset;
|
||||
use Illuminate\Auth\Notifications\ResetPassword;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Str;
|
||||
use Laragear\WebAuthn\WebAuthnAuthentication;
|
||||
use Laravel\Passport\HasApiTokens;
|
||||
|
||||
|
@ -86,6 +89,39 @@ class User extends Authenticatable implements WebAuthnAuthenticatable
|
|||
return $query->where('is_admin', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user is an administrator.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isAdministrator()
|
||||
{
|
||||
return $this->is_admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grant administrator permissions to the user.
|
||||
*
|
||||
* @param bool $promote
|
||||
* @return void
|
||||
*/
|
||||
public function promoteToAdministrator(bool $promote = true)
|
||||
{
|
||||
$this->is_admin = $promote;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset user password with a 12 chars random string.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function resetPassword()
|
||||
{
|
||||
$this->password = Hash::make(Str::password(12));
|
||||
|
||||
event(new PasswordReset($this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the password reset notification.
|
||||
*
|
||||
|
@ -95,8 +131,6 @@ class User extends Authenticatable implements WebAuthnAuthenticatable
|
|||
public function sendPasswordResetNotification($token)
|
||||
{
|
||||
$this->notify(new ResetPassword($token));
|
||||
|
||||
Log::info(sprintf('Password reset token sent to user id "%s', $this->id));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Support\Facades\Lang;
|
||||
|
||||
class TestEmailSettingNotification extends Notification
|
||||
{
|
||||
|
||||
// /**
|
||||
// * The callback that should be used to create the reset password URL.
|
||||
// *
|
||||
// * @var \Closure|null
|
||||
// */
|
||||
// protected static ?Closure $createUrlCallback;
|
||||
|
||||
// /**
|
||||
// * The callback that should be used to build the mail message.
|
||||
// *
|
||||
// * @var \Closure|null
|
||||
// */
|
||||
// protected static ?Closure $toMailCallback;
|
||||
|
||||
/**
|
||||
* TestEmailSettingNotification constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function via($notifiable)
|
||||
{
|
||||
return ['mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
return (new MailMessage)
|
||||
->subject(Lang::get('notifications.test_email_settings.subject'))
|
||||
->greeting(Lang::get('notifications.hello'))
|
||||
->line(
|
||||
Lang::get('notifications.test_email_settings.reason')
|
||||
)
|
||||
->line(
|
||||
Lang::get('notifications.test_email_settings.success')
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class UserObserver
|
||||
{
|
||||
/**
|
||||
* Handle the User "created" event.
|
||||
*/
|
||||
public function created(User $user): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the User "updated" event.
|
||||
*/
|
||||
public function updated(User $user): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the User "deleting" event.
|
||||
*/
|
||||
public function deleting(User $user): bool
|
||||
{
|
||||
Log::info(sprintf('Deletion of User ID #%s requested by User ID #%s', $user->id, Auth::user()->id ?? 'unknown'));
|
||||
|
||||
// Prevent deletion of the only administrator
|
||||
$isLastAdmin = $user->isAdministrator() && User::admins()->count() == 1;
|
||||
|
||||
if ($isLastAdmin) {
|
||||
Log::notice(sprintf('Deletion of user ID #%s refused, cannot delete the only administrator', $user->id));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Deleting user's twofaccounts icon
|
||||
$iconPathes = $user->twofaccounts->filter(function ($twofaccount, $key) {
|
||||
return $twofaccount->icon;
|
||||
})->map(function ($twofaccount, $key) {
|
||||
return $twofaccount->icon;
|
||||
});
|
||||
Storage::disk('icons')->delete($iconPathes->toArray());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the User "deleted" event.
|
||||
*/
|
||||
public function deleted(User $user): void
|
||||
{
|
||||
// DB has cascade delete enabled to flush 2FA and Groups but,
|
||||
// for an unknown reason, SQLite refuses to delete these related.
|
||||
// (despite DB_FOREIGN_KEYS=true which is supposed to enable it)
|
||||
// So it ends with a direct db delete for SQLite...
|
||||
if (DB::getDriverName() === 'sqlite') {
|
||||
DB::table('twofaccounts')->where('user_id', $user->id)->delete();
|
||||
DB::table('groups')->where('user_id', $user->id)->delete();
|
||||
}
|
||||
|
||||
// We flush webauthn credentials & tokens
|
||||
Password::broker('webauthn')->deleteToken($user);
|
||||
$user->flushCredentials();
|
||||
|
||||
// And Passport tokens
|
||||
Password::broker()->deleteToken($user);
|
||||
DB::table('oauth_access_tokens')->where('user_id', $user->id)->delete();
|
||||
|
||||
Log::info(sprintf('User ID #%s and all user traces deleted', $user->id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the User "restored" event.
|
||||
*/
|
||||
public function restored(User $user): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the User "force deleted" event.
|
||||
*/
|
||||
public function forceDeleted(User $user): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
trait SelfTrait
|
||||
{
|
||||
/**
|
||||
* Ownership of single item condition
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isHimself(User $user, mixed $item)
|
||||
{
|
||||
return $user->id === $item->id;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class UserPolicy
|
||||
{
|
||||
use SelfTrait;
|
||||
|
||||
/**
|
||||
* Perform pre-authorization checks.
|
||||
*/
|
||||
public function before(User $user, string $ability): bool|null
|
||||
{
|
||||
if ($user->isAdministrator()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*/
|
||||
public function view(User $user, User $model): bool
|
||||
{
|
||||
$can = $this->isHimself($user, $model);
|
||||
|
||||
if (! $can) {
|
||||
Log::notice(sprintf('User ID #%s cannot view users other than himself)', $user->id));
|
||||
}
|
||||
|
||||
return $can;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*/
|
||||
public function create(?User $user): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*/
|
||||
public function update(User $user, User $model): bool
|
||||
{
|
||||
$can = $this->isHimself($user, $model);
|
||||
|
||||
if (! $can) {
|
||||
Log::notice(sprintf('User ID #%s cannot update users other than himself)', $user->id));
|
||||
}
|
||||
|
||||
return $can;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*/
|
||||
public function delete(User $user, User $model): bool
|
||||
{
|
||||
$can = $this->isHimself($user, $model);
|
||||
|
||||
if (! $can) {
|
||||
Log::notice(sprintf('User ID #%s cannot delete users other than himself)', $user->id));
|
||||
}
|
||||
|
||||
return $can;
|
||||
}
|
||||
}
|
|
@ -29,8 +29,6 @@ class AppServiceProvider extends ServiceProvider
|
|||
*/
|
||||
public function boot()
|
||||
{
|
||||
Blade::withoutComponentTags();
|
||||
|
||||
// Limited to 191 to prevent index length issue with MyISAM and utf8mb4_unicode_ci
|
||||
// when using WAMP (WAMP uses MyISAM as default engine in place of INNOdb)
|
||||
Schema::defaultStringLength(191);
|
||||
|
|
|
@ -6,8 +6,10 @@ use App\Extensions\RemoteUserProvider;
|
|||
use App\Extensions\WebauthnCredentialBroker;
|
||||
use App\Models\Group;
|
||||
use App\Models\TwoFAccount;
|
||||
use App\Models\User;
|
||||
use App\Policies\GroupPolicy;
|
||||
use App\Policies\TwoFAccountPolicy;
|
||||
use App\Policies\UserPolicy;
|
||||
use App\Services\Auth\ReverseProxyGuard;
|
||||
use Illuminate\Auth\Passwords\DatabaseTokenRepository;
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
|
@ -25,6 +27,7 @@ class AuthServiceProvider extends ServiceProvider
|
|||
protected $policies = [
|
||||
TwoFAccount::class => TwoFAccountPolicy::class,
|
||||
Group::class => GroupPolicy::class,
|
||||
User::class => UserPolicy::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,12 +8,16 @@ use App\Events\ScanForNewReleaseCalled;
|
|||
use App\Events\TwoFAccountDeleted;
|
||||
use App\Listeners\CleanIconStorage;
|
||||
use App\Listeners\DissociateTwofaccountFromGroup;
|
||||
use App\Listeners\LogNotification;
|
||||
use App\Listeners\RegisterOpenId;
|
||||
use App\Listeners\ReleaseRadar;
|
||||
use App\Listeners\ResetUsersPreference;
|
||||
use App\Models\User;
|
||||
use App\Observers\UserObserver;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
use Illuminate\Notifications\Events\NotificationSent;
|
||||
use SocialiteProviders\Manager\SocialiteWasCalled;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
|
@ -42,6 +46,18 @@ class EventServiceProvider extends ServiceProvider
|
|||
SocialiteWasCalled::class => [
|
||||
RegisterOpenId::class,
|
||||
],
|
||||
NotificationSent::class => [
|
||||
LogNotification::class,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The model observers for your application.
|
||||
*
|
||||
* @var array<string, string|object|array<int, string|object>>
|
||||
*/
|
||||
protected $observers = [
|
||||
User::class => [UserObserver::class],
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use App\Facades\Settings;
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
class ComplyWithEmailRestrictionPolicy implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
$list = Settings::get('restrictList');
|
||||
$regex = Settings::get('restrictRule');
|
||||
|
||||
$validatesFilter = true;
|
||||
$validatesRegex = true;
|
||||
|
||||
if (Settings::get('restrictRegistration') == true) {
|
||||
if ($list && ! in_array($value, explode('|', $list))) {
|
||||
$validatesFilter = false;
|
||||
}
|
||||
if ($regex && ! preg_match('/' . $regex . '/', $value)) {
|
||||
$validatesRegex = false;
|
||||
}
|
||||
|
||||
if ($list && $regex) {
|
||||
if (! $validatesFilter && ! $validatesRegex) {
|
||||
$fail('validation.custom.email.ComplyWithEmailRestrictionPolicy')->translate();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (! $validatesFilter || ! $validatesRegex) {
|
||||
$fail('validation.custom.email.ComplyWithEmailRestrictionPolicy')->translate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class IsValideEmailList implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
$emails = explode('|', $value);
|
||||
|
||||
$pass = Validator::make(
|
||||
$emails,
|
||||
[
|
||||
'*' => 'email',
|
||||
]
|
||||
)->passes();
|
||||
|
||||
if (! $pass) {
|
||||
$fail('validation.custom.email.IsValidEmailList')->translate();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -52,8 +52,8 @@ class AegisMigrator extends Migrator
|
|||
$parameters['service'] = $otp_parameters['issuer'];
|
||||
$parameters['account'] = $otp_parameters['name'] ?? $parameters['service'];
|
||||
$parameters['secret'] = $this->padToValidBase32Secret($otp_parameters['info']['secret']);
|
||||
$parameters['algorithm'] = $otp_parameters['info']['algo'];
|
||||
$parameters['digits'] = $otp_parameters['info']['digits'];
|
||||
$parameters['algorithm'] = $otp_parameters['info']['algo'] ?? null;
|
||||
$parameters['digits'] = $otp_parameters['info']['digits'] ?? null;
|
||||
$parameters['counter'] = $otp_parameters['info']['counter'] ?? null;
|
||||
$parameters['period'] = $otp_parameters['info']['period'] ?? null;
|
||||
|
||||
|
|
|
@ -86,12 +86,12 @@ class TwoFASMigrator extends Migrator
|
|||
$parameters['service'] = $otp_parameters['name'];
|
||||
$parameters['account'] = $otp_parameters['otp']['account'] ?? $parameters['service'];
|
||||
$parameters['secret'] = $this->padToValidBase32Secret($otp_parameters['secret']);
|
||||
$parameters['algorithm'] = $otp_parameters['otp']['algorithm'];
|
||||
$parameters['digits'] = $otp_parameters['otp']['digits'];
|
||||
$parameters['counter'] = strtolower($parameters['otp_type']) === 'hotp' && $otp_parameters['otp']['counter'] > 0
|
||||
$parameters['algorithm'] = $otp_parameters['otp']['algorithm'] ?? null;
|
||||
$parameters['digits'] = $otp_parameters['otp']['digits'] ?? null;
|
||||
$parameters['counter'] = strtolower($parameters['otp_type']) === 'hotp' && Arr::has($otp_parameters['otp'], 'counter')
|
||||
? $otp_parameters['otp']['counter']
|
||||
: null;
|
||||
$parameters['period'] = strtolower($parameters['otp_type']) === 'totp' && $otp_parameters['otp']['period'] > 0
|
||||
$parameters['period'] = strtolower($parameters['otp_type']) === 'totp' && Arr::has($otp_parameters['otp'], 'period')
|
||||
? $otp_parameters['otp']['period']
|
||||
: null;
|
||||
|
||||
|
|
|
@ -55,8 +55,8 @@ class TwoFAuthMigrator extends Migrator
|
|||
$parameters['service'] = $otp_parameters['service'];
|
||||
$parameters['account'] = $otp_parameters['account'];
|
||||
$parameters['secret'] = $this->padToValidBase32Secret($otp_parameters['secret']);
|
||||
$parameters['algorithm'] = $otp_parameters['algorithm'];
|
||||
$parameters['digits'] = $otp_parameters['digits'];
|
||||
$parameters['algorithm'] = $otp_parameters['algorithm'] ?? null;
|
||||
$parameters['digits'] = $otp_parameters['digits'] ?? null;
|
||||
$parameters['legacy_uri'] = $otp_parameters['legacy_uri'];
|
||||
$parameters['counter'] = strtolower($parameters['otp_type']) === 'hotp' && $otp_parameters['counter'] > 0
|
||||
? $otp_parameters['counter']
|
||||
|
|
|
@ -5,6 +5,9 @@ namespace App\Services;
|
|||
use chillerlan\QRCode\QRCode;
|
||||
use chillerlan\QRCode\QROptions;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Zxing\ChecksumException;
|
||||
use Zxing\FormatException;
|
||||
use Zxing\NotFoundException;
|
||||
use Zxing\QrReader;
|
||||
|
||||
class QrCodeService
|
||||
|
@ -37,12 +40,39 @@ class QrCodeService
|
|||
public static function decode(\Illuminate\Http\UploadedFile $file)
|
||||
{
|
||||
$qrcode = new QrReader($file->get(), QrReader::SOURCE_TYPE_BLOB);
|
||||
$data = urldecode($qrcode->text());
|
||||
$text = $qrcode->text();
|
||||
|
||||
if (! $data) {
|
||||
throw new \App\Exceptions\InvalidQrCodeException;
|
||||
if (! $text) {
|
||||
$text = $qrcode->text([
|
||||
'TRY_HARDER' => true,
|
||||
'NR_ALLOW_SKIP_ROWS' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
// At this point, if we do not have a text, QR code cannot be detected or decoded
|
||||
// so we check the error to provide the user a relevant error message
|
||||
if (! $text) {
|
||||
switch (get_class($qrcode->getError())) {
|
||||
case NotFoundException::class:
|
||||
throw new \App\Exceptions\InvalidQrCodeException(__('errors.cannot_detect_qrcode_in_image'));
|
||||
break;
|
||||
|
||||
case FormatException::class:
|
||||
throw new \App\Exceptions\InvalidQrCodeException(__('errors.cannot_decode_detected_qrcode'));
|
||||
break;
|
||||
|
||||
case ChecksumException::class:
|
||||
throw new \App\Exceptions\InvalidQrCodeException(__('errors.qrcode_has_invalid_checksum'));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \App\Exceptions\InvalidQrCodeException(__('errors.no_readable_qrcode'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$data = urldecode($qrcode->text());
|
||||
|
||||
Log::info('QR code decoded');
|
||||
|
||||
return $data;
|
||||
|
|
94
changelog.md
94
changelog.md
|
@ -1,5 +1,90 @@
|
|||
# Change log
|
||||
|
||||
## [5.1.1] - 2024-03-21
|
||||
|
||||
### Fixed
|
||||
|
||||
- [issue #326](https://github.com/Bubka/2FAuth/issues/326) Admin panel not working when using security device
|
||||
- [issue #327](https://github.com/Bubka/2FAuth/issues/327) "Keep SSO registration enabled" is not saved
|
||||
|
||||
## [5.1.0] - 2024-03-08
|
||||
|
||||
Hey Administrators, this release is for you, a brand new Admin Panel has arrived.
|
||||
|
||||
With this dedicated space, you will be able to manage admin settings previously located in the User Options view (like encryption, version check, registration). Some new settings are available to better control registration (email restrictions and self-ruling SSO) and two new features are coming: Email Configuration Testing and Cache Clearing.
|
||||
|
||||
But the real newness is the user management. All registered accounts are now searchable, the administrator role can be granted to any user, user access (password, personal token, security key/passphrase) can be revoked and you may also delete existing users or even create new ones.
|
||||
|
||||
Note that the 2FAuth API has been updated with the new paths related to user management.
|
||||
|
||||
### Added
|
||||
|
||||
- A user preference to clear search results after copying a code ([#300](https://github.com/Bubka/2FAuth/issues/300)).
|
||||
- A user preference to return to default group after copying a code ([#300](https://github.com/Bubka/2FAuth/issues/300)).
|
||||
- The ability to submit a migration text directly in the Import view besides TXT files & QR codes loading ([#288](https://github.com/Bubka/2FAuth/issues/288)).
|
||||
- An administrator setting to restrict registration to a limited range of email addresses ([#250](https://github.com/Bubka/2FAuth/issues/250)).
|
||||
- An administrator setting to keep user registration via SSO enabled ([#317](https://github.com/Bubka/2FAuth/issues/317)).
|
||||
- A test email feature to ensure email sending works as expected ([#307](https://github.com/Bubka/2FAuth/issues/307)).
|
||||
- A Clear cache feature to... clear the cache, but from the browser ([#316](https://github.com/Bubka/2FAuth/issues/316)).
|
||||
- Hindi translation, thanks to [@saxenas](https://crowdin.com/profile/saxenas)
|
||||
|
||||
### Changed
|
||||
|
||||
- User preferences & Environment variables have been moved from the About view to the new Administration panel ([#303](https://github.com/Bubka/2FAuth/issues/303)).
|
||||
- Spaces are now removed from the Secret when filling out the Advanced form ([#311](https://github.com/Bubka/2FAuth/issues/311)).
|
||||
|
||||
### Fixed
|
||||
|
||||
- [issue #303](https://github.com/Bubka/2FAuth/issues/303) "Already authenticated" error message
|
||||
- [issue #305](https://github.com/Bubka/2FAuth/issues/305) 403 Forbidden {message: "unauthorized"}
|
||||
- [issue #315](https://github.com/Bubka/2FAuth/issues/315) "Check now" button is untranslatable
|
||||
- [issue #320](https://github.com/Bubka/2FAuth/issues/320) app/Policies/OwnershipTrait contains a bug, i think
|
||||
|
||||
### API [1.3.0]
|
||||
|
||||
- `/api/v1/users` paths added to manage registered users
|
||||
- `oauth_provider` property to the response body of `/api/v1/user` GET path
|
||||
|
||||
## [5.0.4] - 2024-02-23
|
||||
|
||||
### Added
|
||||
|
||||
- Japanese translation, thanks to [@yheuhtozr](https://crowdin.com/profile/yheuhtozr)
|
||||
|
||||
### Fixed
|
||||
|
||||
- [issue #284](https://github.com/Bubka/2FAuth/issues/284) Blank screen with version 5.0.3
|
||||
- [issue #296](https://github.com/Bubka/2FAuth/issues/296) WARN Command cancelled (env=production breaks docker entrypoint)
|
||||
- [issue #298](https://github.com/Bubka/2FAuth/issues/298) WebAuthn account recovery and password recovery doesn't work. Email template broken
|
||||
- [issue #299](https://github.com/Bubka/2FAuth/issues/299) OID redirect behind reverse proxy
|
||||
|
||||
## [5.0.3] - 2024-01-19
|
||||
|
||||
⚠️ For everyone experiencing a blank screen after updating to v5.*, please set the `ASSET_URL` env variable to the same value as `APP_URL`.
|
||||
|
||||
### Added
|
||||
|
||||
- The `ASSET_URL` now appears in the .env.example variables next to `APP_URL`
|
||||
|
||||
### Fixed
|
||||
|
||||
- [issue #273](https://github.com/Bubka/2FAuth/issues/273) Unable to automatically paste email and password in login page
|
||||
- [issue #276](https://github.com/Bubka/2FAuth/issues/276) Camera does not work
|
||||
- [issue #277](https://github.com/Bubka/2FAuth/issues/277) Import 2FAS
|
||||
- [issue #279](https://github.com/Bubka/2FAuth/issues/279) Cannot use stdout LOG_CHANNEL anymore
|
||||
|
||||
## [5.0.2] - 2023-12-29
|
||||
|
||||
### Fixed
|
||||
|
||||
- [issue #265](https://github.com/Bubka/2FAuth/issues/265) Version 5.0.1 doesn't display colored countdown segments
|
||||
|
||||
## [5.0.1] - 2023-12-29
|
||||
|
||||
### Fixed
|
||||
|
||||
- [issue #262](https://github.com/Bubka/2FAuth/issues/262) Missing custom base url support
|
||||
|
||||
## [5.0.0] - 2023-12-15
|
||||
|
||||
### 2FAuth v5, the not-so-major release
|
||||
|
@ -13,7 +98,7 @@ Yes, SSO.
|
|||
|
||||
Not so bad, right ?
|
||||
|
||||
The feature, bootstrapped by [@indyKoning](https://github.com/indykoning) with an OpenID provider, has been completed and now provides a Github provider as well. If you need help, the [doc site](https://docs.2fauth.app/security/authenticattion/sso/) has been updated to guide you through the setup process.
|
||||
The feature, bootstrapped by [@indyKoning](https://github.com/indykoning) with an OpenID provider, has been completed and now provides a Github provider as well. I plan to add more providers, tell me in the discussion which ones you would like to see. If you need help, the [docs site](https://docs.2fauth.app/security/authentication/sso/) has been updated to guide you through the setup process.
|
||||
|
||||
v5 also comes with the following.
|
||||
|
||||
|
@ -38,6 +123,11 @@ v5 also comes with the following.
|
|||
|
||||
- [issue #253](https://github.com/Bubka/2FAuth/issues/253) 2FAs exports cannot be imported
|
||||
|
||||
### API [1.2.0]
|
||||
|
||||
- `/api/v1/user` GET path added
|
||||
- `ids` and `withOtp` query parameters added to the `/api/v1/twofaccounts` GET path
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog**: [v4.2.4...v5.0.0](https://github.com/Bubka/2FAuth/compare/v4.2.4...v5.0.0)
|
||||
|
@ -81,7 +171,7 @@ v5 also comes with the following.
|
|||
|
||||
### Changed
|
||||
|
||||
- Navigation with the __Back__ and __Close__ buttons is now fully consistent with their labeling, even when browsing back through successive views using those buttons.
|
||||
- Navigation with the **Back** and **Close** buttons is now fully consistent with their labeling, even when browsing back through successive views using those buttons.
|
||||
- The length of the email submitted during registration is now limited to 191 characters ([#214](https://github.com/Bubka/2FAuth/issues/214)).
|
||||
- Upgrade to Laravel 10
|
||||
|
||||
|
|
|
@ -41,10 +41,10 @@
|
|||
"require-dev": {
|
||||
"barryvdh/laravel-ide-helper": "^2.13",
|
||||
"fakerphp/faker": "^1.21",
|
||||
"larastan/larastan": "^2.0",
|
||||
"laravel/pint": "^1.6",
|
||||
"mockery/mockery": "^1.5",
|
||||
"nunomaduro/collision": "^7.0",
|
||||
"nunomaduro/larastan": "^2.5",
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"phpunit/phpunit": "^10.1",
|
||||
"spatie/laravel-ignition": "^2.0"
|
||||
|
|
|
@ -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": "8ab04001cb3cb872bf0848236af3fc20",
|
||||
"content-hash": "15022c60c2ef59e0821ae0064f477e50",
|
||||
"packages": [
|
||||
{
|
||||
"name": "brick/math",
|
||||
|
@ -2005,16 +2005,16 @@
|
|||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v10.37.3",
|
||||
"version": "v10.38.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "996375dd61f8c6e4ac262b57ed485655d71fcbdc"
|
||||
"reference": "ced4689f495213e9d23995b36098f12a802cc15b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/996375dd61f8c6e4ac262b57ed485655d71fcbdc",
|
||||
"reference": "996375dd61f8c6e4ac262b57ed485655d71fcbdc",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/ced4689f495213e9d23995b36098f12a802cc15b",
|
||||
"reference": "ced4689f495213e9d23995b36098f12a802cc15b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2060,6 +2060,8 @@
|
|||
"voku/portable-ascii": "^2.0"
|
||||
},
|
||||
"conflict": {
|
||||
"carbonphp/carbon-doctrine-types": ">=3.0",
|
||||
"doctrine/dbal": ">=4.0",
|
||||
"tightenco/collect": "<5.5.33"
|
||||
},
|
||||
"provide": {
|
||||
|
@ -2171,6 +2173,7 @@
|
|||
"files": [
|
||||
"src/Illuminate/Collections/helpers.php",
|
||||
"src/Illuminate/Events/functions.php",
|
||||
"src/Illuminate/Filesystem/functions.php",
|
||||
"src/Illuminate/Foundation/helpers.php",
|
||||
"src/Illuminate/Support/helpers.php"
|
||||
],
|
||||
|
@ -2203,7 +2206,7 @@
|
|||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2023-12-13T20:10:58+00:00"
|
||||
"time": "2023-12-20T14:52:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/passport",
|
||||
|
@ -2541,16 +2544,16 @@
|
|||
},
|
||||
{
|
||||
"name": "laravel/ui",
|
||||
"version": "v4.2.3",
|
||||
"version": "v4.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/ui.git",
|
||||
"reference": "eb532ea096ca1c0298c87c19233daf011fda743a"
|
||||
"reference": "d166e09cdcb2e23836f694774cba77a32edb6007"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/ui/zipball/eb532ea096ca1c0298c87c19233daf011fda743a",
|
||||
"reference": "eb532ea096ca1c0298c87c19233daf011fda743a",
|
||||
"url": "https://api.github.com/repos/laravel/ui/zipball/d166e09cdcb2e23836f694774cba77a32edb6007",
|
||||
"reference": "d166e09cdcb2e23836f694774cba77a32edb6007",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2597,9 +2600,9 @@
|
|||
"ui"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/laravel/ui/tree/v4.2.3"
|
||||
"source": "https://github.com/laravel/ui/tree/v4.3.0"
|
||||
},
|
||||
"time": "2023-11-23T14:44:22+00:00"
|
||||
"time": "2023-12-19T14:46:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "lcobucci/clock",
|
||||
|
@ -8553,6 +8556,107 @@
|
|||
},
|
||||
"time": "2020-07-09T08:09:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "larastan/larastan",
|
||||
"version": "v2.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/larastan/larastan.git",
|
||||
"reference": "a2610d46b9999cf558d9900ccb641962d1442f55"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/larastan/larastan/zipball/a2610d46b9999cf558d9900ccb641962d1442f55",
|
||||
"reference": "a2610d46b9999cf558d9900ccb641962d1442f55",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"illuminate/console": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/container": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/contracts": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/database": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/http": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/pipeline": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/support": "^9.52.16 || ^10.28.0",
|
||||
"php": "^8.0.2",
|
||||
"phpmyadmin/sql-parser": "^5.8.2",
|
||||
"phpstan/phpstan": "^1.10.41"
|
||||
},
|
||||
"require-dev": {
|
||||
"nikic/php-parser": "^4.17.1",
|
||||
"orchestra/canvas": "^7.11.1 || ^8.11.0",
|
||||
"orchestra/testbench": "^7.33.0 || ^8.13.0",
|
||||
"phpunit/phpunit": "^9.6.13"
|
||||
},
|
||||
"suggest": {
|
||||
"orchestra/testbench": "Using Larastan for analysing a package needs Testbench"
|
||||
},
|
||||
"type": "phpstan-extension",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0-dev"
|
||||
},
|
||||
"phpstan": {
|
||||
"includes": [
|
||||
"extension.neon"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Larastan\\Larastan\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Can Vural",
|
||||
"email": "can9119@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Nuno Maduro",
|
||||
"email": "enunomaduro@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan wrapper for Laravel",
|
||||
"keywords": [
|
||||
"PHPStan",
|
||||
"code analyse",
|
||||
"code analysis",
|
||||
"larastan",
|
||||
"laravel",
|
||||
"package",
|
||||
"php",
|
||||
"static analysis"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/larastan/larastan/issues",
|
||||
"source": "https://github.com/larastan/larastan/tree/v2.7.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://www.paypal.com/paypalme/enunomaduro",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/canvural",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nunomaduro",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/nunomaduro",
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-04T19:21:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/pint",
|
||||
"version": "v1.13.7",
|
||||
|
@ -8857,108 +8961,6 @@
|
|||
],
|
||||
"time": "2023-10-11T15:45:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nunomaduro/larastan",
|
||||
"version": "v2.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/larastan/larastan.git",
|
||||
"reference": "a2610d46b9999cf558d9900ccb641962d1442f55"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/larastan/larastan/zipball/a2610d46b9999cf558d9900ccb641962d1442f55",
|
||||
"reference": "a2610d46b9999cf558d9900ccb641962d1442f55",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"illuminate/console": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/container": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/contracts": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/database": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/http": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/pipeline": "^9.52.16 || ^10.28.0",
|
||||
"illuminate/support": "^9.52.16 || ^10.28.0",
|
||||
"php": "^8.0.2",
|
||||
"phpmyadmin/sql-parser": "^5.8.2",
|
||||
"phpstan/phpstan": "^1.10.41"
|
||||
},
|
||||
"require-dev": {
|
||||
"nikic/php-parser": "^4.17.1",
|
||||
"orchestra/canvas": "^7.11.1 || ^8.11.0",
|
||||
"orchestra/testbench": "^7.33.0 || ^8.13.0",
|
||||
"phpunit/phpunit": "^9.6.13"
|
||||
},
|
||||
"suggest": {
|
||||
"orchestra/testbench": "Using Larastan for analysing a package needs Testbench"
|
||||
},
|
||||
"type": "phpstan-extension",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0-dev"
|
||||
},
|
||||
"phpstan": {
|
||||
"includes": [
|
||||
"extension.neon"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Larastan\\Larastan\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Can Vural",
|
||||
"email": "can9119@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Nuno Maduro",
|
||||
"email": "enunomaduro@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan wrapper for Laravel",
|
||||
"keywords": [
|
||||
"PHPStan",
|
||||
"code analyse",
|
||||
"code analysis",
|
||||
"larastan",
|
||||
"laravel",
|
||||
"package",
|
||||
"php",
|
||||
"static analysis"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/larastan/larastan/issues",
|
||||
"source": "https://github.com/larastan/larastan/tree/v2.7.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://www.paypal.com/paypalme/enunomaduro",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/canvural",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nunomaduro",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/nunomaduro",
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"abandoned": "larastan/larastan",
|
||||
"time": "2023-12-04T19:21:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
"version": "2.0.3",
|
||||
|
@ -9270,16 +9272,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpstan/phpdoc-parser",
|
||||
"version": "1.24.4",
|
||||
"version": "1.24.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpdoc-parser.git",
|
||||
"reference": "6bd0c26f3786cd9b7c359675cb789e35a8e07496"
|
||||
"reference": "fedf211ff14ec8381c9bf5714e33a7a552dd1acc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6bd0c26f3786cd9b7c359675cb789e35a8e07496",
|
||||
"reference": "6bd0c26f3786cd9b7c359675cb789e35a8e07496",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fedf211ff14ec8381c9bf5714e33a7a552dd1acc",
|
||||
"reference": "fedf211ff14ec8381c9bf5714e33a7a552dd1acc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -9311,9 +9313,9 @@
|
|||
"description": "PHPDoc parser with support for nullable, intersection and generic types",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
|
||||
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.4"
|
||||
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.5"
|
||||
},
|
||||
"time": "2023-11-26T18:29:22+00:00"
|
||||
"time": "2023-12-16T09:33:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
|
@ -9379,23 +9381,23 @@
|
|||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "10.1.10",
|
||||
"version": "10.1.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "599109c8ca6bae97b23482d557d2874c25a65e59"
|
||||
"reference": "78c3b7625965c2513ee96569a4dbb62601784145"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/599109c8ca6bae97b23482d557d2874c25a65e59",
|
||||
"reference": "599109c8ca6bae97b23482d557d2874c25a65e59",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/78c3b7625965c2513ee96569a4dbb62601784145",
|
||||
"reference": "78c3b7625965c2513ee96569a4dbb62601784145",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"nikic/php-parser": "^4.15",
|
||||
"nikic/php-parser": "^4.18 || ^5.0",
|
||||
"php": ">=8.1",
|
||||
"phpunit/php-file-iterator": "^4.0",
|
||||
"phpunit/php-text-template": "^3.0",
|
||||
|
@ -9445,7 +9447,7 @@
|
|||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.10"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.11"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -9453,7 +9455,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-11T06:28:43+00:00"
|
||||
"time": "2023-12-21T15:38:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
|
@ -10045,20 +10047,20 @@
|
|||
},
|
||||
{
|
||||
"name": "sebastian/complexity",
|
||||
"version": "3.1.0",
|
||||
"version": "3.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/complexity.git",
|
||||
"reference": "68cfb347a44871f01e33ab0ef8215966432f6957"
|
||||
"reference": "68ff824baeae169ec9f2137158ee529584553799"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957",
|
||||
"reference": "68cfb347a44871f01e33ab0ef8215966432f6957",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799",
|
||||
"reference": "68ff824baeae169ec9f2137158ee529584553799",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"nikic/php-parser": "^4.10",
|
||||
"nikic/php-parser": "^4.18 || ^5.0",
|
||||
"php": ">=8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
|
@ -10067,7 +10069,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "3.1-dev"
|
||||
"dev-main": "3.2-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -10091,7 +10093,7 @@
|
|||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/complexity/issues",
|
||||
"security": "https://github.com/sebastianbergmann/complexity/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0"
|
||||
"source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -10099,7 +10101,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-09-28T11:50:59+00:00"
|
||||
"time": "2023-12-21T08:37:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/diff",
|
||||
|
@ -10374,20 +10376,20 @@
|
|||
},
|
||||
{
|
||||
"name": "sebastian/lines-of-code",
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/lines-of-code.git",
|
||||
"reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d"
|
||||
"reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d",
|
||||
"reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0",
|
||||
"reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"nikic/php-parser": "^4.10",
|
||||
"nikic/php-parser": "^4.18 || ^5.0",
|
||||
"php": ">=8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
|
@ -10420,7 +10422,7 @@
|
|||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
|
||||
"security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1"
|
||||
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -10428,7 +10430,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-08-31T09:25:50+00:00"
|
||||
"time": "2023-12-21T08:38:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/object-enumerator",
|
||||
|
@ -10931,16 +10933,16 @@
|
|||
},
|
||||
{
|
||||
"name": "spatie/laravel-ignition",
|
||||
"version": "2.3.1",
|
||||
"version": "2.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/laravel-ignition.git",
|
||||
"reference": "bf21cd15aa47fa4ec5d73bbc932005c70261efc8"
|
||||
"reference": "66499cd3c858642ded56dafb8fa0352057ca20dd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/bf21cd15aa47fa4ec5d73bbc932005c70261efc8",
|
||||
"reference": "bf21cd15aa47fa4ec5d73bbc932005c70261efc8",
|
||||
"url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/66499cd3c858642ded56dafb8fa0352057ca20dd",
|
||||
"reference": "66499cd3c858642ded56dafb8fa0352057ca20dd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -11019,7 +11021,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-10-09T12:55:26+00:00"
|
||||
"time": "2023-12-21T09:43:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
|
@ -11092,5 +11094,5 @@
|
|||
"ext-xml": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.3.0"
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'version' => '5.0.0',
|
||||
'version' => '5.1.1',
|
||||
'repository' => 'https://github.com/Bubka/2FAuth',
|
||||
'latestReleaseUrl' => 'https://api.github.com/repos/Bubka/2FAuth/releases/latest',
|
||||
'installDocUrl' => 'https://docs.2fauth.app/getting-started/installation/self-hosted-server/',
|
||||
|
@ -56,6 +56,8 @@ return [
|
|||
'es',
|
||||
'bg',
|
||||
'ru',
|
||||
'ja',
|
||||
'hi',
|
||||
],
|
||||
|
||||
/*
|
||||
|
@ -73,6 +75,8 @@ return [
|
|||
'latestRelease' => false,
|
||||
'disableRegistration' => false,
|
||||
'enableSso' => true,
|
||||
'restrictRegistration' => false,
|
||||
'keepSsoRegistrationEnabled' => false,
|
||||
],
|
||||
|
||||
/*
|
||||
|
@ -88,12 +92,14 @@ return [
|
|||
'revealDottedOTP' => false,
|
||||
'closeOtpOnCopy' => false,
|
||||
'copyOtpOnDisplay' => false,
|
||||
'clearSearchOnCopy' => false,
|
||||
'useBasicQrcodeReader' => false,
|
||||
'displayMode' => 'list',
|
||||
'showAccountsIcons' => true,
|
||||
'kickUserAfter' => 15,
|
||||
'activeGroup' => 0,
|
||||
'rememberActiveGroup' => true,
|
||||
'viewDefaultGroupOnCopy' => false,
|
||||
'defaultGroup' => 0,
|
||||
'defaultCaptureMode' => 'livescan',
|
||||
'useDirectCapture' => false,
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
use Illuminate\Support\Facades\Facade;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
$appUrl = env('APP_URL', env('HEROKU_APP_NAME') ? 'https://' . env('HEROKU_APP_NAME') . '.herokuapp.com' : 'http://localhost');
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|
@ -55,9 +57,9 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'url' => env('APP_URL', env('HEROKU_APP_NAME') ? 'https://' . env('HEROKU_APP_NAME') . '.herokuapp.com' : 'http://localhost'),
|
||||
'url' => $appUrl,
|
||||
|
||||
'asset_url' => env('ASSET_URL', null),
|
||||
'asset_url' => env('ASSET_URL', $appUrl),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
|
@ -27,19 +27,19 @@ return [
|
|||
'userinfo_url' => env('OPENID_USERINFO_URL'),
|
||||
'client_id' => env('OPENID_CLIENT_ID'),
|
||||
'client_secret' => env('OPENID_CLIENT_SECRET'),
|
||||
'redirect' => '/socialite/callback/openid',
|
||||
'redirect' => env('APP_URL') . '/socialite/callback/openid',
|
||||
],
|
||||
|
||||
'github' => [
|
||||
'client_id' => env('GITHUB_CLIENT_ID'),
|
||||
'client_secret' => env('GITHUB_CLIENT_SECRET'),
|
||||
'redirect' => '/socialite/callback/github',
|
||||
'redirect' => env('APP_URL') . '/socialite/callback/github',
|
||||
],
|
||||
|
||||
// 'google' => [
|
||||
// 'client_id' => env('GOOGLE_CLIENT_ID'),
|
||||
// 'client_secret' => env('GOOGLE_CLIENT_SECRET'),
|
||||
// 'redirect' => '/socialite/callback/google ',
|
||||
// 'redirect' => env('APP_URL') . '/socialite/callback/google ',
|
||||
// ],
|
||||
|
||||
'postmark' => [
|
||||
|
|
|
@ -24,13 +24,18 @@ services:
|
|||
# This variable must match your installation's external address.
|
||||
# Webauthn won't work otherwise.
|
||||
- APP_URL=http://localhost
|
||||
# If you want to serve js assets from a CDN (like https://cdn.example.com),
|
||||
# uncomment the following line and set this var with the CDN url.
|
||||
# Otherwise, let this line commented.
|
||||
# - ASSET_URL=http://localhost
|
||||
#
|
||||
# Turn this to true if you want your app to react like a demo.
|
||||
# The Demo mode reset the app content every hours and set a generic demo user.
|
||||
- IS_DEMO_APP=false
|
||||
# The log channel defines where your log entries go to.
|
||||
# 'daily' is the default logging mode giving you 5 daily rotated log files in /storage/logs/.
|
||||
# Several other options exist. You can use 'single' for one big fat error log (not recommended).
|
||||
# Also available are 'syslog', 'errorlog' and 'stdout' which will log to the system itself.
|
||||
# 'daily' is the default logging mode giving you 7 daily rotated log files in /storage/logs/.
|
||||
# Also available are 'errorlog', 'syslog', 'stderr', 'papertrail', 'slack' and a 'stack' channel
|
||||
# to combine multiple channels into a single one.
|
||||
- LOG_CHANNEL=daily
|
||||
# Log level. You can set this from least severe to most severe:
|
||||
# debug, info, notice, warning, error, critical, alert, emergency
|
||||
|
|
|
@ -9,12 +9,31 @@ echo "supervisord version: $(supervisord version)"
|
|||
php-fpm81 -v | head -n 1
|
||||
nginx -v
|
||||
|
||||
# Database creation
|
||||
if [ "${DB_CONNECTION}" = "sqlite" ]; then
|
||||
if [ ! -f /2fauth/database.sqlite ]; then
|
||||
touch /2fauth/database.sqlite
|
||||
# DB_DATABASE is trimmed if necessary
|
||||
if [[ $DB_DATABASE == \"* ]] && [[ $DB_DATABASE == *\" ]] ; then
|
||||
dbpath=${DB_DATABASE:1:${#DB_DATABASE}-2}
|
||||
else
|
||||
dbpath=${DB_DATABASE}
|
||||
fi
|
||||
if [ $dbpath != "/srv/database/database.sqlite" ]; then
|
||||
echo "DB_DATABASE sets with custom path: ${dbpath}"
|
||||
if [ ! -f ${dbpath} ]; then
|
||||
echo "${dbpath} does not exist, we create it"
|
||||
touch ${dbpath}
|
||||
fi
|
||||
else
|
||||
echo "DB_DATABASE sets with default path, we will use a symlink"
|
||||
echo "Actual db file will be /2fauth/database.sqlite"
|
||||
if [ ! -f /2fauth/database.sqlite ]; then
|
||||
echo "/2fauth/database.sqlite does not exist, we create it"
|
||||
touch /2fauth/database.sqlite
|
||||
fi
|
||||
rm -f /srv/database/database.sqlite
|
||||
ln -s /2fauth/database.sqlite /srv/database/database.sqlite
|
||||
echo "/srv/database/database.sqlite is now a symlink to /2fauth/database.sqlite"
|
||||
fi
|
||||
rm -f /srv/database/database.sqlite
|
||||
ln -s /2fauth/database.sqlite /srv/database/database.sqlite
|
||||
fi
|
||||
|
||||
# Inject storage in /2fauth and use it with a symlink
|
||||
|
@ -30,16 +49,20 @@ if [ -f /2fauth/installed ]; then
|
|||
INSTALLED_COMMIT="$(cat /2fauth/installed)"
|
||||
if [ "${INSTALLED_COMMIT}" != "${COMMIT}" ]; then
|
||||
echo "Installed commit ${INSTALLED_COMMIT} is different from program commit ${COMMIT}, we are migrating..."
|
||||
php artisan migrate
|
||||
php artisan cache:clear
|
||||
php artisan config:clear
|
||||
php artisan migrate --force
|
||||
fi
|
||||
else
|
||||
php artisan migrate:refresh
|
||||
php artisan migrate:refresh --force
|
||||
php artisan passport:install
|
||||
fi
|
||||
|
||||
echo "${COMMIT}" > /2fauth/installed
|
||||
php artisan storage:link --quiet
|
||||
php artisan optimize:clear
|
||||
php artisan config:cache
|
||||
php artisan route:cache
|
||||
php artisan view:cache
|
||||
|
||||
supervisord
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
memory_limit=2048M
|
|
@ -0,0 +1 @@
|
|||
import{Q as b,u as k,e as i,f as g,k as C,p as r,h as s,t,m as o,n as c,j as e,i as h,U as u}from"./app-2d89b28f.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const B={class:"title has-text-grey-dark"},F={class:"block"},A=s("span",{class:"is-size-5"},"2FAuth",-1),w=s("br",null,null,-1),y=s("img",{class:"about-logo",src:"logo.svg",alt:"2FAuth logo"},null,-1),v=s("p",{class:"block"},[e(" ©Bubka "),s("a",{class:"is-size-7",href:"https://github.com/Bubka/2FAuth/blob/master/LICENSE"},"AGPL-3.0 license")],-1),I={class:"title is-5 has-text-grey-light"},L={class:"buttons"},$={class:"icon is-small"},S=s("span",null,"Github",-1),T={class:"icon is-small"},V=s("span",null,"Docs",-1),z={class:"icon is-small"},N=s("span",null,"Demo",-1),D={class:"icon is-small"},E=s("span",null,"API",-1),W={class:"title is-5 has-text-grey-light"},j={class:"block"},M=s("a",{href:"https://docs.2fauth.app/credits/"},"Laravel, Bulma CSS, Vue.js and more",-1),R=s("a",{href:"https://fontawesome.com/"},"Font Awesome",-1),x=s("a",{class:"is-size-7",href:"https://fontawesome.com/license/free"},"(CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)",-1),G=s("a",{href:"https://2fa.directory/"},"2FA Directory",-1),P=s("a",{class:"is-size-7",href:"https://github.com/2factorauth/twofactorauth/blob/master/LICENSE.md"},"(MIT License)",-1),q={__name:"About",setup(U){const _=b("2fauth"),d=k().options.history.state.back;return(a,Q)=>{const l=i("FontAwesomeIcon"),p=i("ButtonBackCloseCancel"),m=i("VueFooter"),f=i("ResponsiveWidthWrapper");return g(),C(f,null,{default:r(()=>[s("h1",B,t(a.$t("commons.about")),1),s("p",F,[o(h(u),null,{default:r(({mode:n})=>[s("span",{class:c(n=="dark"?"has-text-white":"has-text-black")},[A,e(" v"+t(h(_).version),1)],2)]),_:1}),w,e(" "+t(a.$t("commons.2fauth_teaser")),1)]),y,v,s("h2",I,t(a.$t("commons.resources")),1),s("div",L,[o(h(u),null,{default:r(({mode:n})=>[s("a",{class:c(["button",{"is-dark":n=="dark"}]),href:"https://github.com/Bubka/2FAuth",target:"_blank"},[s("span",$,[o(l,{icon:["fab","github-alt"]})]),S],2),s("a",{class:c(["button",{"is-dark":n=="dark"}]),href:"https://docs.2fauth.app/",target:"_blank"},[s("span",T,[o(l,{icon:["fas","book"]})]),V],2),s("a",{class:c(["button",{"is-dark":n=="dark"}]),href:"https://demo.2fauth.app/",target:"_blank"},[s("span",z,[o(l,{icon:["fas","flask"]})]),N],2),s("a",{class:c(["button",{"is-dark":n=="dark"}]),href:"https://docs.2fauth.app/resources/rapidoc.html",target:"_blank"},[s("span",D,[o(l,{icon:["fas","code"]})]),E],2)]),_:1})]),s("h2",W,t(a.$t("commons.credits")),1),s("p",j,[s("ul",null,[s("li",null,[e(t(a.$t("commons.made_with"))+" ",1),M]),s("li",null,[e(t(a.$t("commons.ui_icons_by"))+" ",1),R,e(" "),x]),s("li",null,[e(t(a.$t("commons.logos_by"))+" ",1),G,e(" "),P])])]),o(m,{showButtons:!0},{default:r(()=>[o(p,{returnTo:{path:h(d)},action:"back"},null,8,["returnTo"])]),_:1})]),_:1})}}};export{q as default};
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
import{r as u,e as t,f as s,g as a,m as n,p as i,h as o,F as _,G as v,i as f,n as h,j as k,t as w}from"./app-2d89b28f.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const T={class:"options-header"},g={class:"tabs is-centered is-fullwidth"},b={__name:"AdminTabs",props:{activeTab:{type:String,default:""}},setup(r){const p=r,d=u([{name:"admin.app_setup",view:"admin.appSetup",id:"lnkTabApp"},{name:"admin.users",view:"admin.users",id:"lnkTabUsers"}]);return(c,R)=>{const l=t("RouterLink"),m=t("ResponsiveWidthWrapper");return s(),a("div",T,[n(m,null,{default:i(()=>[o("div",g,[o("ul",null,[(s(!0),a(_,null,v(f(d),e=>(s(),a("li",{key:e.view,class:h({"is-active":e.view===p.activeTab})},[n(l,{id:e.id,to:{name:e.view}},{default:i(()=>[k(w(c.$t(e.name)),1)]),_:2},1032,["id","to"])],2))),128))])])]),_:1})])}}};export{b as _};
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
import{b as r,S as p,e as l,f as m,g as _,m as u,D as b,I as d}from"./app-2d89b28f.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const y=["aria-label","title"],C={__name:"CopyButton",props:{token:String},setup(e){const s=e,n=r(),{copy:c}=p({legacy:!0});function a(){c(s.token),n.success({text:d("commons.copied_to_clipboard")})}return(o,t)=>{const i=l("FontAwesomeIcon");return m(),_("button",{"aria-label":o.$t("commons.copy_to_clipboard"),title:o.$t("commons.copy_to_clipboard"),class:"button is-like-text is-pulled-right is-small is-text",onClick:t[0]||(t[0]=b(f=>a(),["stop"]))},[u(i,{icon:["fas","copy"]})],8,y)}}};export{C as _};
|
|
@ -0,0 +1 @@
|
|||
import{b as _,u as F,d as V,e as n,f as b,g,m as r,p as y,h as x,i as o,D as B,I as h}from"./app-2d89b28f.js";import{F as C}from"./Form-5283f7b6.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const E=["onSubmit"],k={__name:"Create",setup(N){const m=_(),l=F(),e=V(new C({name:"",email:"",password:"",password_confirmation:"",is_admin:!1}));async function i(d){e.password_confirmation=e.password,e.post("/api/v1/users").then(s=>{const t=s.data;m.success({text:h("admin.user_created")}),l.push({name:"admin.manageUser",params:{userId:t.info.id}})})}return(d,s)=>{const t=n("FormField"),u=n("FormPasswordField"),p=n("FormCheckbox"),f=n("FormButtons"),c=n("FormWrapper"),w=n("VueFooter");return b(),g("div",null,[r(c,{title:"admin.new_user"},{default:y(()=>[x("form",{onSubmit:B(i,["prevent"]),onKeydown:s[4]||(s[4]=a=>o(e).onKeydown(a))},[r(t,{modelValue:o(e).name,"onUpdate:modelValue":s[0]||(s[0]=a=>o(e).name=a),fieldName:"name",fieldError:o(e).errors.get("name"),inputType:"text",label:"auth.forms.name",maxLength:255,autofocus:""},null,8,["modelValue","fieldError"]),r(t,{modelValue:o(e).email,"onUpdate:modelValue":s[1]||(s[1]=a=>o(e).email=a),fieldName:"email",fieldError:o(e).errors.get("email"),inputType:"email",label:"auth.forms.email",maxLength:255},null,8,["modelValue","fieldError"]),r(u,{modelValue:o(e).password,"onUpdate:modelValue":s[2]||(s[2]=a=>o(e).password=a),fieldName:"password",fieldError:o(e).errors.get("password"),showRules:!0,label:"auth.forms.password",autocomplete:"new-password"},null,8,["modelValue","fieldError"]),r(p,{modelValue:o(e).is_admin,"onUpdate:modelValue":s[3]||(s[3]=a=>o(e).is_admin=a),fieldName:"is_admin",label:"admin.forms.is_admin.label",help:"admin.forms.is_admin.help"},null,8,["modelValue"]),r(f,{isBusy:o(e).isBusy,isDisabled:o(e).isDisabled,showCancelButton:!0,cancelLandingView:"admin.users",caption:"commons.create",submitId:"btnCreateUser"},null,8,["isBusy","isDisabled","cancelLandingView"])],40,E)]),_:1}),r(w)])}}};export{k as default};
|
|
@ -1 +1 @@
|
|||
import{S as v,u as G,Z as h,v as w,d as y,_ as N,K as S,e as m,f as E,k as I,p as V,i as t,h as C,m as i,D as $}from"./app-1b332c21.js";import{F as k}from"./Form-940b5f6c.js";import{u as K}from"./bus-84126a4e.js";/*! 2FAuth version 5.0.0 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const M=["onSubmit"],W={__name:"CreateUpdate",props:{groupId:[Number,String]},setup(c){const s=c,p=v(),d=G(),l=h(),a=K(),r=w(()=>s.groupId!=null),o=y(new k({name:""}));N(()=>{l.name=="editGroup"&&(a.editedGroupName?(o.name=a.editedGroupName,a.editedGroupName=void 0):S.get(s.groupId).then(e=>{o.name=e.data.name}))});function f(){r.value?B():g()}async function g(){o.post("/api/v1/groups").then(e=>{p.addOrEdit(e.data),d.push({name:"groups"})})}async function B(){o.put("/api/v1/groups/"+s.groupId).then(e=>{p.addOrEdit(e.data),d.push({name:"groups"})})}return(e,n)=>{const b=m("FormField"),F=m("FormButtons"),_=m("FormWrapper");return E(),I(_,{title:t(r)?e.$t("groups.forms.rename_group"):e.$t("groups.forms.new_group")},{default:V(()=>[C("form",{onSubmit:$(f,["prevent"]),onKeydown:n[1]||(n[1]=u=>t(o).onKeydown(u))},[i(b,{modelValue:t(o).name,"onUpdate:modelValue":n[0]||(n[0]=u=>t(o).name=u),fieldName:"name",fieldError:t(o).errors.get("name"),label:"commons.name",autofocus:""},null,8,["modelValue","fieldError"]),i(F,{submitId:t(r)?"btnEditGroup":"btnCreateGroup",isBusy:t(o).isBusy,caption:t(r)?e.$t("commons.save"):e.$t("commons.create"),showCancelButton:!0,cancelLandingView:"groups"},null,8,["submitId","isBusy","caption"])],40,M)]),_:1},8,["title"])}}};export{W as default};
|
||||
import{T as v,u as G,_ as h,v as w,d as y,$ as N,L as E,e as m,f as I,k as S,p as V,i as t,h as C,m as i,D as $}from"./app-2d89b28f.js";import{F as k}from"./Form-5283f7b6.js";import{u as M}from"./bus-7802a020.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const K=["onSubmit"],U={__name:"CreateUpdate",props:{groupId:[Number,String]},setup(c){const s=c,p=v(),d=G(),l=h(),a=M(),r=w(()=>s.groupId!=null),o=y(new k({name:""}));N(()=>{l.name=="editGroup"&&(a.editedGroupName?(o.name=a.editedGroupName,a.editedGroupName=void 0):E.get(s.groupId).then(e=>{o.name=e.data.name}))});function f(){r.value?B():g()}async function g(){o.post("/api/v1/groups").then(e=>{p.addOrEdit(e.data),d.push({name:"groups"})})}async function B(){o.put("/api/v1/groups/"+s.groupId).then(e=>{p.addOrEdit(e.data),d.push({name:"groups"})})}return(e,n)=>{const b=m("FormField"),F=m("FormButtons"),_=m("FormWrapper");return I(),S(_,{title:t(r)?e.$t("groups.forms.rename_group"):e.$t("groups.forms.new_group")},{default:V(()=>[C("form",{onSubmit:$(f,["prevent"]),onKeydown:n[1]||(n[1]=u=>t(o).onKeydown(u))},[i(b,{modelValue:t(o).name,"onUpdate:modelValue":n[0]||(n[0]=u=>t(o).name=u),fieldName:"name",fieldError:t(o).errors.get("name"),label:"commons.name",autofocus:""},null,8,["modelValue","fieldError"]),i(F,{submitId:t(r)?"btnEditGroup":"btnCreateGroup",isBusy:t(o).isBusy,caption:t(r)?e.$t("commons.save"):e.$t("commons.create"),showCancelButton:!0,cancelLandingView:"groups"},null,8,["submitId","isBusy","caption"])],40,K)]),_:1},8,["title"])}}};export{U as default};
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
import{u as h,b as w,d as b,I as a,e as s,f as y,k as F,p as v,h as B,m as r,i as t,D as g}from"./app-1b332c21.js";import{F as V}from"./Form-940b5f6c.js";/*! 2FAuth version 5.0.0 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const C=["onSubmit"],I={__name:"Edit",props:{credentialId:{type:String,default:""}},setup(i){const u=i,m=h(),d=w(),e=b(new V({name:a("auth.webauthn.my_device")}));function c(){e.patch("/webauthn/credentials/"+u.credentialId+"/name").then(()=>{d.success({text:a("auth.webauthn.device_successfully_registered")}),m.push({name:"settings.webauthn.devices"})})}return(l,n)=>{const p=s("FormField"),f=s("FormButtons"),_=s("FormWrapper");return y(),F(_,{title:"auth.webauthn.rename_device"},{default:v(()=>[B("form",{onSubmit:g(c,["prevent"]),onKeydown:n[1]||(n[1]=o=>t(e).onKeydown(o))},[r(p,{modelValue:t(e).name,"onUpdate:modelValue":n[0]||(n[0]=o=>t(e).name=o),fieldName:"name",fieldError:t(e).errors.get("name"),inputType:"text",label:"commons.new_name",autofocus:""},null,8,["modelValue","fieldError"]),r(f,{submitId:"btnEditCredential",isBusy:t(e).isBusy,caption:l.$t("commons.save"),showCancelButton:!0,cancelLandingView:"settings.webauthn.devices"},null,8,["isBusy","caption"])],40,C)]),_:1})}}};export{I as default};
|
||||
import{u as h,b as w,d as b,I as a,e as s,f as y,k as F,p as v,h as B,m as r,i as t,D as g}from"./app-2d89b28f.js";import{F as V}from"./Form-5283f7b6.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const C=["onSubmit"],I={__name:"Edit",props:{credentialId:{type:String,default:""}},setup(i){const u=i,m=h(),d=w(),e=b(new V({name:a("auth.webauthn.my_device")}));function c(){e.patch("/webauthn/credentials/"+u.credentialId+"/name").then(()=>{d.success({text:a("auth.webauthn.device_successfully_registered")}),m.push({name:"settings.webauthn.devices"})})}return(l,n)=>{const p=s("FormField"),f=s("FormButtons"),_=s("FormWrapper");return y(),F(_,{title:"auth.webauthn.rename_device"},{default:v(()=>[B("form",{onSubmit:g(c,["prevent"]),onKeydown:n[1]||(n[1]=o=>t(e).onKeydown(o))},[r(p,{modelValue:t(e).name,"onUpdate:modelValue":n[0]||(n[0]=o=>t(e).name=o),fieldName:"name",fieldError:t(e).errors.get("name"),inputType:"text",label:"commons.new_name",autofocus:""},null,8,["modelValue","fieldError"]),r(f,{submitId:"btnEditCredential",isBusy:t(e).isBusy,caption:l.$t("commons.save"),showCancelButton:!0,cancelLandingView:"settings.webauthn.devices"},null,8,["isBusy","caption"])],40,C)]),_:1})}}};export{I as default};
|
|
@ -1 +1 @@
|
|||
import{b as y,u as b,Z as k,r as v,v as V,x as w,o as N,I as x,e as B,f as r,g as t,m as M,p as $,i as e,E as q,h as l,t as n,l as c,j as C}from"./app-1b332c21.js";/*! 2FAuth version 5.0.0 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const E={key:0,class:"error-message"},R=l("p",{class:"error-404"},null,-1),D={key:1,class:"error-message"},F=l("p",{class:"error-generic"},null,-1),S={key:0,class:"has-text-grey-lighter"},j={key:1,class:"has-text-grey-lighter"},z={key:2,class:"is-size-7 is-family-code"},H=l("br",null,null,-1),T={__name:"Error",props:{closable:{type:Boolean,default:!0}},setup(m){const p=m,s=y(),d=b(),a=k(),u=v(!0),_=V(()=>!1);w(u,o=>{o==!1&&g()}),N(()=>{a.query.err&&(s.message=x("errors."+a.query.err))});function g(){window.history.length>1&&a.name!=="404"&&a.name!=="notFound"&&!a.query.err?d.go(-1):d.push({name:"accounts"})}return(o,i)=>{const h=B("modal");return r(),t("div",null,[M(h,{modelValue:e(u),"onUpdate:modelValue":i[0]||(i[0]=f=>q(u)?u.value=f:null),closable:p.closable},{default:$(()=>[o.$route.name=="404"||o.$route.name=="notFound"?(r(),t("div",E,[R,l("p",null,n(o.$t("errors.resource_not_found")),1)])):(r(),t("div",D,[F,l("p",null,n(o.$t("errors.error_occured")),1),e(s).message?(r(),t("p",S,n(e(s).message),1)):c("",!0),e(s).originalMessage?(r(),t("p",j,n(e(s).originalMessage),1)):c("",!0),e(_)&&e(s).debug?(r(),t("p",z,[H,C(n(e(s).debug),1)])):c("",!0)]))]),_:1},8,["modelValue","closable"])])}}};export{T as default};
|
||||
import{b as y,u as b,_ as k,r as v,v as V,x as w,o as N,I as x,e as B,f as r,g as t,m as M,p as $,i as e,E as q,h as l,t as n,l as c,j as C}from"./app-2d89b28f.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const E={key:0,class:"error-message"},R=l("p",{class:"error-404"},null,-1),D={key:1,class:"error-message"},F=l("p",{class:"error-generic"},null,-1),S={key:0,class:"has-text-grey-lighter"},j={key:1,class:"has-text-grey-lighter"},z={key:2,class:"is-size-7 is-family-code"},H=l("br",null,null,-1),T={__name:"Error",props:{closable:{type:Boolean,default:!0}},setup(m){const p=m,s=y(),d=b(),a=k(),u=v(!0),_=V(()=>!1);w(u,o=>{o==!1&&g()}),N(()=>{a.query.err&&(s.message=x("errors."+a.query.err))});function g(){window.history.length>1&&a.name!=="404"&&a.name!=="notFound"&&!a.query.err?d.go(-1):d.push({name:"accounts"})}return(o,i)=>{const h=B("modal");return r(),t("div",null,[M(h,{modelValue:e(u),"onUpdate:modelValue":i[0]||(i[0]=f=>q(u)?u.value=f:null),closable:p.closable},{default:$(()=>[o.$route.name=="404"||o.$route.name=="notFound"?(r(),t("div",E,[R,l("p",null,n(o.$t("errors.resource_not_found")),1)])):(r(),t("div",D,[F,l("p",null,n(o.$t("errors.error_occured")),1),e(s).message?(r(),t("p",S,n(e(s).message),1)):c("",!0),e(s).originalMessage?(r(),t("p",j,n(e(s).originalMessage),1)):c("",!0),e(_)&&e(s).debug?(r(),t("p",z,[H,C(n(e(s).debug),1)])):c("",!0)]))]),_:1},8,["modelValue","closable"])])}}};export{T as default};
|
|
@ -1 +1 @@
|
|||
import{a1 as l}from"./app-1b332c21.js";/*! 2FAuth version 5.0.0 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */class f{constructor(){this.errors={}}set(t,s){typeof t=="object"?this.errors=t:this.set({...this.errors,[t]:c(s)})}all(){return this.errors}has(t){return this.errors.hasOwnProperty(t)}hasAny(...t){return t.some(s=>this.has(s))}any(){return Object.keys(this.errors).length>0}get(t){if(this.has(t))return this.getAll(t)[0]}getAll(t){return c(this.errors[t]||[])}only(...t){const s=[];return t.forEach(r=>{const e=this.get(r);e&&s.push(e)}),s}flatten(){return Object.values(this.errors).reduce((t,s)=>t.concat(s),[])}clear(t){const s={};t&&Object.keys(this.errors).forEach(r=>{r!==t&&(s[r]=this.errors[r])}),this.set(s)}}function c(o){return Array.isArray(o)?o:[o]}class i{constructor(t={}){this.axios=l("web"),this.isBusy=!1,this.isDisabled=!1,this.errors=new f,this.originalData=this.deepCopy(t),Object.assign(this,t)}fill(t){this.keys().forEach(s=>{this[s]=t[s]})}setOriginal(){Object.keys(this).filter(t=>!i.ignore.includes(t)).forEach(t=>{this.originalData[t]=this.deepCopy(this[t])})}hasChanged(){return this.keys().some(t=>this[t]!==this.originalData[t])}fillWithKeyValueObject(t){this.keys().forEach(s=>{const r=t.find(e=>e.key===s.toString());r!=null&&(this[s]=r.value)})}data(){return this.keys().reduce((t,s)=>({...t,[s]:this[s]}),{})}keys(){return Object.keys(this).filter(t=>!i.ignore.includes(t))}startProcessing(){this.errors.clear(),this.isBusy=!0}finishProcessing(){this.isBusy=!1}clear(){this.errors.clear()}reset(){Object.keys(this).filter(t=>!i.ignore.includes(t)).forEach(t=>{this[t]=this.deepCopy(this.originalData[t])})}get(t,s={}){return this.submit("get",t,s)}post(t,s={}){return this.submit("post",t,s)}patch(t,s={}){return this.submit("patch",t,s)}put(t,s={}){return this.submit("put",t,s)}delete(t,s={}){return this.submit("delete",t,s)}submit(t,s,r={}){this.startProcessing();const e=t==="get"?{params:this.data()}:this.data();return new Promise((a,u)=>{this.axios.request({url:this.route(s),method:t,data:e,...r}).then(h=>{this.finishProcessing(),a(h)}).catch(h=>{var n;this.isBusy=!1,h.response&&this.errors.set(this.extractErrors(h.response)),((n=h.response)==null?void 0:n.status)!=422&&u(h)})})}upload(t,s={}){return this.startProcessing(),new Promise((r,e)=>{this.axios.post(this.route(t),this.data(),{headers:{"Content-Type":"multipart/form-data"},...s}).then(a=>{this.finishProcessing(),r(a)}).catch(a=>{this.isBusy=!1,a.response&&this.errors.set(this.extractErrors(a.response)),e(a)})})}extractErrors(t){return!t.data||typeof t.data!="object"?{error:i.errorMessage}:t.data.errors?{...t.data.errors}:t.data.message?{error:t.data.message}:{...t.data}}route(t,s={}){let r=t;return i.routes.hasOwnProperty(t)&&(r=decodeURI(i.routes[t])),typeof s!="object"&&(s={id:s}),Object.keys(s).forEach(e=>{r=r.replace(`{${e}}`,s[e])}),r}onKeydown(t){t.target.name&&this.errors.clear(t.target.name)}deepCopy(t){if(t===null||typeof t!="object")return t;const s=Array.isArray(t)?[]:{};return Object.keys(t).forEach(r=>{s[r]=this.deepCopy(t[r])}),s}}i.routes={};i.errorMessage="Something went wrong. Please try again.";i.ignore=["isBusy","isDisabled","errors","originalData","axios"];export{i as F};
|
||||
import{a4 as l}from"./app-2d89b28f.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */class f{constructor(){this.errors={}}set(t,s){typeof t=="object"?this.errors=t:this.set({...this.errors,[t]:c(s)})}all(){return this.errors}has(t){return this.errors.hasOwnProperty(t)}hasAny(...t){return t.some(s=>this.has(s))}any(){return Object.keys(this.errors).length>0}get(t){if(this.has(t))return this.getAll(t)[0]}getAll(t){return c(this.errors[t]||[])}only(...t){const s=[];return t.forEach(r=>{const e=this.get(r);e&&s.push(e)}),s}flatten(){return Object.values(this.errors).reduce((t,s)=>t.concat(s),[])}clear(t){const s={};t&&Object.keys(this.errors).forEach(r=>{r!==t&&(s[r]=this.errors[r])}),this.set(s)}}function c(o){return Array.isArray(o)?o:[o]}class i{constructor(t={}){this.axios=l("web"),this.isBusy=!1,this.isDisabled=!1,this.errors=new f,this.originalData=this.deepCopy(t),Object.assign(this,t)}fill(t){this.keys().forEach(s=>{this[s]=t[s]})}setOriginal(){Object.keys(this).filter(t=>!i.ignore.includes(t)).forEach(t=>{this.originalData[t]=this.deepCopy(this[t])})}hasChanged(){return this.keys().some(t=>this[t]!==this.originalData[t])}fillWithKeyValueObject(t){this.keys().forEach(s=>{const r=t.find(e=>e.key===s.toString());r!=null&&(this[s]=r.value)})}data(){return this.keys().reduce((t,s)=>({...t,[s]:this[s]}),{})}keys(){return Object.keys(this).filter(t=>!i.ignore.includes(t))}startProcessing(){this.errors.clear(),this.isBusy=!0}finishProcessing(){this.isBusy=!1}clear(){this.errors.clear()}reset(){Object.keys(this).filter(t=>!i.ignore.includes(t)).forEach(t=>{this[t]=this.deepCopy(this.originalData[t])})}get(t,s={}){return this.submit("get",t,s)}post(t,s={}){return this.submit("post",t,s)}patch(t,s={}){return this.submit("patch",t,s)}put(t,s={}){return this.submit("put",t,s)}delete(t,s={}){return this.submit("delete",t,s)}submit(t,s,r={}){this.startProcessing();const e=t==="get"?{params:this.data()}:this.data();return new Promise((a,u)=>{this.axios.request({url:this.route(s),method:t,data:e,...r}).then(h=>{this.finishProcessing(),a(h)}).catch(h=>{var n;this.isBusy=!1,h.response&&this.errors.set(this.extractErrors(h.response)),((n=h.response)==null?void 0:n.status)!=422&&u(h)})})}upload(t,s={}){return this.startProcessing(),new Promise((r,e)=>{this.axios.post(this.route(t),this.data(),{headers:{"Content-Type":"multipart/form-data"},...s}).then(a=>{this.finishProcessing(),r(a)}).catch(a=>{this.isBusy=!1,a.response&&this.errors.set(this.extractErrors(a.response)),e(a)})})}extractErrors(t){return!t.data||typeof t.data!="object"?{error:i.errorMessage}:t.data.errors?{...t.data.errors}:t.data.message?{error:t.data.message}:{...t.data}}route(t,s={}){let r=t;return i.routes.hasOwnProperty(t)&&(r=decodeURI(i.routes[t])),typeof s!="object"&&(s={id:s}),Object.keys(s).forEach(e=>{r=r.replace(`{${e}}`,s[e])}),r}onKeydown(t){t.target.name&&this.errors.clear(t.target.name)}deepCopy(t){if(t===null||typeof t!="object")return t;const s=Array.isArray(t)?[]:{};return Object.keys(t).forEach(r=>{s[r]=this.deepCopy(t[r])}),s}}i.routes={};i.errorMessage="Something went wrong. Please try again.";i.ignore=["isBusy","isDisabled","errors","originalData","axios"];export{i as F};
|
|
@ -0,0 +1 @@
|
|||
import{u as C,T as $,r as w,o as z,a0 as G,e as c,f as l,k as b,p as u,h as o,t as a,m as s,j as _,i,g as m,F,G as R,n as V,U as N,l as h}from"./app-2d89b28f.js";import{u as E}from"./bus-7802a020.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const I={class:"title has-text-grey-dark"},L={class:"is-size-7-mobile"},T={class:"mt-3 mb-6"},W={key:0},A=["onClick","title"],x={class:"is-family-primary is-size-6 is-size-7-mobile has-text-grey"},M={class:"mt-2 is-size-7 is-pulled-right"},S={key:1,class:"has-text-centered"},U={class:"is-size-4"},J={__name:"Groups",setup(j){C();const t=$(),f=E(),p=w(!1);return z(async()=>{p.value=t.isEmpty,await t.fetch().finally(()=>{p.value=!1})}),G((e,g)=>{var n;e.name=="editGroup"&&(f.editedGroupName=(n=t.items.find(d=>d.id==e.params.groupId))==null?void 0:n.name)}),(e,g)=>{const n=c("FontAwesomeIcon"),d=c("RouterLink"),k=c("ButtonBackCloseCancel"),y=c("VueFooter"),v=c("ResponsiveWidthWrapper");return l(),b(v,null,{default:u(()=>[o("h1",I,a(e.$t("groups.groups")),1),o("div",L,a(e.$t("groups.manage_groups_legend")),1),o("div",T,[s(d,{class:"is-link mt-5",to:{name:"createGroup"}},{default:u(()=>[s(n,{icon:["fas","plus-circle"]}),_(" "+a(e.$t("groups.create_group")),1)]),_:1})]),i(t).isEmpty?h("",!0):(l(),m("div",W,[(l(!0),m(F,null,R(i(t).withoutTheAllGroup,r=>(l(),m("div",{key:r.id,class:"group-item is-size-5 is-size-6-mobile"},[_(a(r.name)+" ",1),s(i(N),null,{default:u(({mode:B})=>[o("button",{class:V(["button tag is-pulled-right",B=="dark"?"is-dark":"is-white"]),onClick:q=>i(t).delete(r.id),title:e.$t("commons.delete")},a(e.$t("commons.delete")),11,A)]),_:2},1024),s(d,{to:{name:"editGroup",params:{groupId:r.id}},class:"has-text-grey px-1",title:e.$t("commons.rename")},{default:u(()=>[s(n,{icon:["fas","pen-square"]})]),_:2},1032,["to","title"]),o("span",x,a(e.$t("groups.x_accounts",{count:r.twofaccounts_count})),1)]))),128)),o("div",M,a(e.$t("groups.deleting_group_does_not_delete_accounts")),1)])),i(p)&&i(t).isEmpty?(l(),m("div",S,[o("span",U,[s(n,{icon:["fas","spinner"],spin:""})])])):h("",!0),s(y,{showButtons:!0},{default:u(()=>[s(k,{returnTo:{name:"accounts"},action:"close"})]),_:1})]),_:1})}}};export{J as default};
|
|
@ -1 +0,0 @@
|
|||
import{u as C,S as $,r as w,o as z,$ as G,e as c,f as l,k as b,p as u,h as a,t as s,m as t,j as _,i,g as m,F,G as R,n as V,U as N,l as h}from"./app-1b332c21.js";import{u as E}from"./bus-84126a4e.js";/*! 2FAuth version 5.0.0 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const I={class:"title has-text-grey-dark"},L={class:"is-size-7-mobile"},W={class:"mt-3 mb-6"},A={key:0},S=["onClick","title"],T={class:"is-family-primary is-size-6 is-size-7-mobile has-text-grey"},M={class:"mt-2 is-size-7 is-pulled-right"},U={key:1,class:"has-text-centered"},j={class:"is-size-4"},J={__name:"Groups",setup(q){C();const o=$(),f=E(),p=w(!1);return z(async()=>{p.value=o.isEmpty,await o.fetch().finally(()=>{p.value=!1})}),G((e,g)=>{var n;e.name=="editGroup"&&(f.editedGroupName=(n=o.items.find(d=>d.id==e.params.groupId))==null?void 0:n.name)}),(e,g)=>{const n=c("FontAwesomeIcon"),d=c("RouterLink"),k=c("ButtonBackCloseCancel"),y=c("VueFooter"),v=c("ResponsiveWidthWrapper");return l(),b(v,null,{default:u(()=>[a("h1",I,s(e.$t("groups.groups")),1),a("div",L,s(e.$t("groups.manage_groups_legend")),1),a("div",W,[t(d,{class:"is-link mt-5",to:{name:"createGroup"}},{default:u(()=>[t(n,{icon:["fas","plus-circle"]}),_(" "+s(e.$t("groups.create_group")),1)]),_:1})]),i(o).isEmpty?h("",!0):(l(),m("div",A,[(l(!0),m(F,null,R(i(o).withoutTheAllGroup,r=>(l(),m("div",{key:r.id,class:"group-item is-size-5 is-size-6-mobile"},[_(s(r.name)+" ",1),t(i(N),null,{default:u(({mode:B})=>[a("button",{class:V(["button tag is-pulled-right",B=="dark"?"is-dark":"is-white"]),onClick:x=>i(o).delete(r.id),title:e.$t("commons.delete")},s(e.$t("commons.delete")),11,S)]),_:2},1024),t(d,{to:{name:"editGroup",params:{groupId:r.id}},class:"has-text-grey px-1",title:e.$t("commons.rename")},{default:u(()=>[t(n,{icon:["fas","pen-square"]})]),_:2},1032,["to","title"]),a("span",T,s(r.twofaccounts_count)+" "+s(e.$t("twofaccounts.accounts")),1)]))),128)),a("div",M,s(e.$t("groups.deleting_group_does_not_delete_accounts")),1)])),i(p)&&i(o).isEmpty?(l(),m("div",U,[a("span",j,[t(n,{icon:["fas","spinner"],spin:""})])])):h("",!0),t(y,{showButtons:!0},{default:u(()=>[t(k,{returnTo:{name:"accounts"},action:"close"})]),_:1})]),_:1})}}};export{J as default};
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +0,0 @@
|
|||
import{P as D,b as G,a2 as H,R as O,r as m,o as q,d as J,$ as Q,e as _,f as l,g as c,m as n,h as t,p as f,i as o,l as d,t as a,w as X,j as g,F as Y,G as Z,k as ee,U as se,n as z,D as A,I as y}from"./app-1b332c21.js";import{F as te}from"./Form-940b5f6c.js";import{u as S}from"./userService-5f2b5050.js";import{_ as oe}from"./SettingTabs-52d14fa3.js";import{S as ne}from"./Spinner-b3cbad3a.js";/*! 2FAuth version 5.0.0 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const ae={class:"options-tabs"},ie=["innerHTML"],le={class:"title is-4 has-text-grey-light"},re={class:"is-size-7-mobile"},ce={class:"mt-3"},ue=["onKeyup"],de={key:1},me={class:"tags is-pulled-right"},_e=["onClick"],fe=["onClick","title"],pe={key:1,class:"is-size-7-mobile is-size-6 my-3"},ve={key:2,class:"pat is-family-monospace is-size-6 is-size-7-mobile has-text-success"},he={class:"mt-2 is-size-7 is-pulled-right"},ke={key:0,class:"is-overlay modal-otp modal-background"},ge={class:"main-section"},ye=["onSubmit"],be={class:"field is-grouped"},Te={class:"control"},Ce={class:"control"},Ae={__name:"OAuth",setup(we){const P=D("2fauth"),p=G(),x=H(P.prefix+"returnTo","accounts"),{copy:E}=O({legacy:!0}),r=m([]),b=m(!1),T=m(!1),v=m(!1),h=m(null),C=m(null);q(()=>{w()});const u=J(new te({name:""}));function w(){b.value=!0,S.getPersonalAccessTokens({returnError:!0}).then(e=>{r.value=[],e.data.forEach(i=>{i.id===C.value?(i.value=h.value,r.value.unshift(i)):r.value.push(i)})}).catch(e=>{e.response.status===405?T.value=!0:p.error(e)}).finally(()=>{b.value=!1,C.value=null,h.value=null})}function F(){N(),T.value?p.warn({text:y("errors.unsupported_with_reverseproxy")}):v.value=!0}function L(){u.post("/oauth/personal-access-tokens").then(e=>{h.value=e.data.accessToken,C.value=e.data.token.id,w(),v.value=!1,u.reset()})}function M(e){confirm(y("settings.confirm.revoke"))&&S.deletePersonalAccessToken(e).then(i=>{r.value=r.value.filter(k=>k.id!==e),p.success({text:y("settings.token_revoked")})})}function N(){r.value.forEach(e=>{e.value=null}),h.value=null}function K(e){E(e),p.success({text:y("commons.copied_to_clipboard")})}function I(){v.value=!1,u.reset()}return Q(e=>{e.name.startsWith("settings.")||p.clear()}),(e,i)=>{const k=_("FontAwesomeIcon"),U=_("ButtonBackCloseCancel"),R=_("VueFooter"),$=_("FormWrapper"),W=_("FormField"),V=_("VueButton");return l(),c("div",null,[n(oe,{activeTab:"settings.oauth.tokens"},null,8,["activeTab"]),t("div",ae,[n($,null,{default:f(()=>[o(T)?(l(),c("div",{key:0,class:"notification is-warning has-text-centered",innerHTML:e.$t("auth.auth_handled_by_proxy")},null,8,ie)):d("",!0),t("h4",le,a(e.$t("settings.personal_access_tokens")),1),t("div",re,a(e.$t("settings.token_legend")),1),t("div",ce,[t("a",{tabindex:"0",class:"is-link",onClick:F,onKeyup:X(F,["enter"])},[n(k,{icon:["fas","plus-circle"]}),g(" "+a(e.$t("settings.generate_new_token")),1)],40,ue)]),o(r).length>0?(l(),c("div",de,[(l(!0),c(Y,null,Z(o(r),s=>(l(),c("div",{key:s.id,class:"group-item is-size-5 is-size-6-mobile"},[s.value?(l(),ee(k,{key:0,class:"has-text-success",icon:["fas","check"]})):d("",!0),g(" "+a(s.name)+" ",1),t("div",me,[n(o(se),null,{default:f(({mode:B})=>[s.value?(l(),c("button",{key:0,class:z(["button tag",{"is-link":B!="dark"}]),onClick:A(j=>K(s.value),["stop"])},a(e.$t("commons.copy")),11,_e)):d("",!0),t("button",{class:z(["button tag",B==="dark"?"is-dark":"is-white"]),onClick:j=>M(s.id),title:e.$t("settings.revoke")},a(e.$t("settings.revoke")),11,fe)]),_:2},1024)]),s.value?(l(),c("span",pe,a(e.$t("settings.make_sure_copy_token")),1)):d("",!0),s.value?(l(),c("span",ve,a(s.value),1)):d("",!0)]))),128)),t("div",he,a(e.$t("settings.revoking_a_token_is_permanent")),1)])):d("",!0),n(ne,{isVisible:o(b)&&o(r).length===0},null,8,["isVisible"]),n(R,{showButtons:!0},{default:f(()=>[n(U,{returnTo:{name:o(x)},action:"close"},null,8,["returnTo"])]),_:1})]),_:1})]),o(v)?(l(),c("div",ke,[t("main",ge,[n($,{title:"settings.forms.new_token"},{default:f(()=>[t("form",{onSubmit:A(L,["prevent"]),onKeydown:i[1]||(i[1]=s=>o(u).onKeydown(s))},[n(W,{modelValue:o(u).name,"onUpdate:modelValue":i[0]||(i[0]=s=>o(u).name=s),fieldName:"name",fieldError:o(u).errors.get("name"),inputType:"text",label:"commons.name",autofocus:""},null,8,["modelValue","fieldError"]),t("div",be,[t("div",Te,[n(V,{id:"btnGenerateToken",isLoading:o(u).isBusy},{default:f(()=>[g(a(e.$t("commons.generate")),1)]),_:1},8,["isLoading"])]),t("div",Ce,[n(V,{onClick:I,nativeType:"button",id:"btnCancel",color:"is-text"},{default:f(()=>[g(a(e.$t("commons.cancel")),1)]),_:1})])])],40,ye)]),_:1})])])):d("",!0)])}}};export{Ae as default};
|
|
@ -0,0 +1 @@
|
|||
import{Q as G,b as H,a2 as R,S as J,r as m,o as O,d as Q,J as z,a0 as q,e as _,f as l,g as c,m as n,h as t,p as f,i as o,l as d,t as a,w as X,j as g,F as Y,G as Z,k as ee,U as se,n as A,D as S,I as y}from"./app-2d89b28f.js";import{F as te}from"./Form-5283f7b6.js";import{_ as oe}from"./SettingTabs-708dbaa6.js";import{S as ne}from"./Spinner-aea2b665.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const ae={class:"options-tabs"},ie=["innerHTML"],le={class:"title is-4 has-text-grey-light"},re={class:"is-size-7-mobile"},ce={class:"mt-3"},ue=["onKeyup"],de={key:1},me={class:"tags is-pulled-right"},_e=["onClick"],fe=["onClick","title"],pe={key:1,class:"is-size-7-mobile is-size-6 my-3"},ve={key:2,class:"pat is-family-monospace is-size-6 is-size-7-mobile has-text-success"},he={class:"mt-2 is-size-7 is-pulled-right"},ke={key:0,class:"is-overlay modal-otp modal-background"},ge={class:"main-section"},ye=["onSubmit"],be={class:"field is-grouped"},Te={class:"control"},Ce={class:"control"},ze={__name:"OAuth",setup(we){const x=G("2fauth"),p=H(),E=R(x.prefix+"returnTo","accounts"),{copy:L}=J({legacy:!0}),r=m([]),b=m(!1),T=m(!1),v=m(!1),h=m(null),C=m(null);O(()=>{w()});const u=Q(new te({name:""}));function w(){b.value=!0,z.getPersonalAccessTokens({returnError:!0}).then(e=>{r.value=[],e.data.forEach(i=>{i.id===C.value?(i.value=h.value,r.value.unshift(i)):r.value.push(i)})}).catch(e=>{e.response.status===405?T.value=!0:p.error(e)}).finally(()=>{b.value=!1,C.value=null,h.value=null})}function F(){P(),T.value?p.warn({text:y("errors.unsupported_with_reverseproxy")}):v.value=!0}function M(){u.post("/oauth/personal-access-tokens").then(e=>{h.value=e.data.accessToken,C.value=e.data.token.id,w(),v.value=!1,u.reset()})}function N(e){confirm(y("settings.confirm.revoke"))&&z.deletePersonalAccessToken(e).then(i=>{r.value=r.value.filter(k=>k.id!==e),p.success({text:y("settings.token_revoked")})})}function P(){r.value.forEach(e=>{e.value=null}),h.value=null}function K(e){L(e),p.success({text:y("commons.copied_to_clipboard")})}function I(){v.value=!1,u.reset()}return q(e=>{e.name.startsWith("settings.")||p.clear()}),(e,i)=>{const k=_("FontAwesomeIcon"),U=_("ButtonBackCloseCancel"),W=_("VueFooter"),V=_("FormWrapper"),j=_("FormField"),$=_("VueButton");return l(),c("div",null,[n(oe,{activeTab:"settings.oauth.tokens"},null,8,["activeTab"]),t("div",ae,[n(V,null,{default:f(()=>[o(T)?(l(),c("div",{key:0,class:"notification is-warning has-text-centered",innerHTML:e.$t("auth.auth_handled_by_proxy")},null,8,ie)):d("",!0),t("h4",le,a(e.$t("settings.personal_access_tokens")),1),t("div",re,a(e.$t("settings.token_legend")),1),t("div",ce,[t("a",{tabindex:"0",class:"is-link",onClick:F,onKeyup:X(F,["enter"])},[n(k,{icon:["fas","plus-circle"]}),g(" "+a(e.$t("settings.generate_new_token")),1)],40,ue)]),o(r).length>0?(l(),c("div",de,[(l(!0),c(Y,null,Z(o(r),s=>(l(),c("div",{key:s.id,class:"group-item is-size-5 is-size-6-mobile"},[s.value?(l(),ee(k,{key:0,class:"has-text-success",icon:["fas","check"]})):d("",!0),g(" "+a(s.name)+" ",1),t("div",me,[n(o(se),null,{default:f(({mode:B})=>[s.value?(l(),c("button",{key:0,class:A(["button tag",{"is-link":B!="dark"}]),onClick:S(D=>K(s.value),["stop"])},a(e.$t("commons.copy")),11,_e)):d("",!0),t("button",{class:A(["button tag",B==="dark"?"is-dark":"is-white"]),onClick:D=>N(s.id),title:e.$t("settings.revoke")},a(e.$t("settings.revoke")),11,fe)]),_:2},1024)]),s.value?(l(),c("span",pe,a(e.$t("settings.make_sure_copy_token")),1)):d("",!0),s.value?(l(),c("span",ve,a(s.value),1)):d("",!0)]))),128)),t("div",he,a(e.$t("settings.revoking_a_token_is_permanent")),1)])):d("",!0),n(ne,{isVisible:o(b)&&o(r).length===0},null,8,["isVisible"]),n(W,{showButtons:!0},{default:f(()=>[n(U,{returnTo:{name:o(E)},action:"close"},null,8,["returnTo"])]),_:1})]),_:1})]),o(v)?(l(),c("div",ke,[t("main",ge,[n(V,{title:"settings.forms.new_token"},{default:f(()=>[t("form",{onSubmit:S(M,["prevent"]),onKeydown:i[1]||(i[1]=s=>o(u).onKeydown(s))},[n(j,{modelValue:o(u).name,"onUpdate:modelValue":i[0]||(i[0]=s=>o(u).name=s),fieldName:"name",fieldError:o(u).errors.get("name"),inputType:"text",label:"commons.name",autofocus:""},null,8,["modelValue","fieldError"]),t("div",be,[t("div",Te,[n($,{id:"btnGenerateToken",isLoading:o(u).isBusy},{default:f(()=>[g(a(e.$t("commons.generate")),1)]),_:1},8,["isLoading"])]),t("div",Ce,[n($,{onClick:I,nativeType:"button",id:"btnCancel",color:"is-text"},{default:f(()=>[g(a(e.$t("commons.cancel")),1)]),_:1})])])],40,ye)]),_:1})])])):d("",!0)])}}};export{ze as default};
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
import{u as _,Z as m,r as p,_ as f,J as h,e as c,f as n,g as r,h as a,i as t,l as B,m as s,p as g}from"./app-1b332c21.js";import{S as k}from"./Spinner-b3cbad3a.js";/*! 2FAuth version 5.0.0 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const v={class:"modal modal-otp is-active"},C=a("div",{class:"modal-background"},null,-1),V={class:"modal-content"},w={class:"has-text-centered m-5"},b=["src","alt"],F={__name:"QRcode",setup(y){_();const l=m(),e=p();f(()=>{i()});async function i(){const{data:o}=await h.getQrcode(l.params.twofaccountId);e.value=o.qrcode}return(o,R)=>{const u=c("ButtonBackCloseCancel"),d=c("VueFooter");return n(),r("div",v,[C,a("div",V,[a("p",w,[t(e)?(n(),r("img",{key:0,src:t(e),class:"has-background-light",alt:o.$t("commons.image_of_qrcode_to_scan")},null,8,b)):B("",!0),s(k,{isVisible:!t(e),type:"raw",class:"is-size-1"},null,8,["isVisible"])])]),s(d,{showButtons:!0,internalFooterType:"modal"},{default:g(()=>[s(u,{returnTo:{name:"accounts"},action:"close"})]),_:1})])}}};export{F as default};
|
||||
import{u as _,_ as m,r as p,$ as f,K as h,e as c,f as n,g as r,h as a,i as t,l as B,m as s,p as g}from"./app-2d89b28f.js";import{S as k}from"./Spinner-aea2b665.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const v={class:"modal modal-otp is-active"},C=a("div",{class:"modal-background"},null,-1),V={class:"modal-content"},w={class:"has-text-centered m-5"},b=["src","alt"],F={__name:"QRcode",setup(y){_();const l=m(),e=p();f(()=>{i()});async function i(){const{data:o}=await h.getQrcode(l.params.twofaccountId);e.value=o.qrcode}return(o,R)=>{const u=c("ButtonBackCloseCancel"),d=c("VueFooter");return n(),r("div",v,[C,a("div",V,[a("p",w,[t(e)?(n(),r("img",{key:0,src:t(e),class:"has-background-light",alt:o.$t("commons.image_of_qrcode_to_scan")},null,8,b)):B("",!0),s(k,{isVisible:!t(e),type:"raw",class:"is-size-1"},null,8,["isVisible"])])]),s(d,{showButtons:!0,internalFooterType:"modal"},{default:g(()=>[s(u,{returnTo:{name:"accounts"},action:"close"})]),_:1})])}}};export{F as default};
|
|
@ -1 +1 @@
|
|||
import{R as h,b as v,e as w,f as r,g as l,h as n,t as s,m as c,p as _,n as y,i as d,I as u,U as g,D as q,l as m,F as $}from"./app-1b332c21.js";/*! 2FAuth version 5.0.0 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const x=n("div",{class:"too-bad"},null,-1),E={class:"block"},I={key:0,class:"block has-text-link"},B={class:"button is-link is-outlined is-rounded"},F={class:"icon is-small"},R={__name:"QrContentDisplay",props:{qrContent:String},setup(o){const{copy:b}=h({legacy:!0}),k=v();function p(t){var e=/^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/,a=new RegExp(e);return a.test(t)}function C(t){const e=document.createElement("a");e.setAttribute("href",t),e.dispatchEvent(new MouseEvent("click",{view:window,bubbles:!0,cancelable:!0}))}function f(t){b(t),k.success({text:u("commons.copied_to_clipboard")})}return(t,e)=>{const a=w("FontAwesomeIcon");return r(),l($,null,[x,n("div",E,s(t.$t("errors.data_of_qrcode_is_not_valid_URI")),1),c(d(g),null,{default:_(({mode:i})=>[n("div",{class:y(["block mb-6",i=="dark"?"has-text-light":"has-text-grey-dark"])},s(o.qrContent?o.qrContent:"["+("trans"in t?t.trans:d(u))("commons.nothing")+"]"),3)]),_:1}),o.qrContent?(r(),l("div",I,[n("button",{class:"button is-link is-outlined is-rounded",onClick:e[0]||(e[0]=q(i=>f(o.qrContent),["stop"]))},s(t.$t("commons.copy_to_clipboard")),1)])):m("",!0),p(o.qrContent)?(r(),l("div",{key:1,class:"block has-text-link",onClick:e[1]||(e[1]=i=>C(o.qrContent))},[n("button",B,[n("span",null,s(t.$t("commons.open_in_browser")),1),n("span",F,[c(a,{icon:["fas","external-link-alt"]})])])])):m("",!0)],64)}}};export{R as _};
|
||||
import{S as h,b as v,e as w,f as r,g as l,h as n,t as s,m as c,p as _,n as y,i as d,I as u,U as g,D as q,l as m,F as $}from"./app-2d89b28f.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const x=n("div",{class:"too-bad"},null,-1),E={class:"block"},I={key:0,class:"block has-text-link"},B={class:"button is-link is-outlined is-rounded"},F={class:"icon is-small"},S={__name:"QrContentDisplay",props:{qrContent:String},setup(o){const{copy:b}=h({legacy:!0}),k=v();function p(t){var e=/^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/,a=new RegExp(e);return a.test(t)}function C(t){const e=document.createElement("a");e.setAttribute("href",t),e.dispatchEvent(new MouseEvent("click",{view:window,bubbles:!0,cancelable:!0}))}function f(t){b(t),k.success({text:u("commons.copied_to_clipboard")})}return(t,e)=>{const a=w("FontAwesomeIcon");return r(),l($,null,[x,n("div",E,s(t.$t("errors.data_of_qrcode_is_not_valid_URI")),1),c(d(g),null,{default:_(({mode:i})=>[n("div",{class:y(["block mb-6",i=="dark"?"has-text-light":"has-text-grey-dark"])},s(o.qrContent?o.qrContent:"["+("trans"in t?t.trans:d(u))("commons.nothing")+"]"),3)]),_:1}),o.qrContent?(r(),l("div",I,[n("button",{class:"button is-link is-outlined is-rounded",onClick:e[0]||(e[0]=q(i=>f(o.qrContent),["stop"]))},s(t.$t("commons.copy_to_clipboard")),1)])):m("",!0),p(o.qrContent)?(r(),l("div",{key:1,class:"block has-text-link",onClick:e[1]||(e[1]=i=>C(o.qrContent))},[n("button",B,[n("span",null,s(t.$t("commons.open_in_browser")),1),n("span",F,[c(a,{icon:["fas","external-link-alt"]})])])])):m("",!0)],64)}}};export{S as _};
|
|
@ -0,0 +1 @@
|
|||
import{Q as V,b as B,u as R,_ as g,a2 as $,d as N,a0 as C,e as a,f as D,k as S,p as d,h as u,D as q,i as s,m as l,j as c,t as m,I as A}from"./app-2d89b28f.js";import{F as L}from"./Form-5283f7b6.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const W=["onSubmit"],x={class:"field"},I={__name:"Recover",setup(E){const p=V("2fauth"),r=B(),f=R(),i=g(),h=$(p.prefix+"showWebauthnForm",!1),o=N(new L({email:i.query.email,password:"",token:i.query.token,revokeAll:!1}));function _(t){r.clear(),o.post("/webauthn/recover",{returnError:!0}).then(e=>{h.value=!1,f.push({name:"login"})}).catch(e=>{e.response.status===401?r.alert({text:A("auth.forms.authentication_failed"),duration:-1}):e.response.status===422?r.alert({text:e.response.data.message,duration:-1}):r.error(e)})}return C(()=>{r.clear()}),(t,e)=>{const w=a("FormCheckbox"),b=a("FormPasswordField"),v=a("RouterLink"),F=a("FormButtons"),k=a("VueFooter"),y=a("FormWrapper");return D(),S(y,{title:t.$t("auth.webauthn.account_recovery"),punchline:t.$t("auth.webauthn.recover_account_instructions")},{default:d(()=>[u("div",null,[u("form",{onSubmit:q(_,["prevent"]),onKeydown:e[2]||(e[2]=n=>s(o).onKeydown(n))},[l(w,{modelValue:s(o).revokeAll,"onUpdate:modelValue":e[0]||(e[0]=n=>s(o).revokeAll=n),fieldName:"revokeAll",label:"auth.webauthn.disable_all_security_devices",help:"auth.webauthn.disable_all_security_devices_help"},null,8,["modelValue"]),l(b,{modelValue:s(o).password,"onUpdate:modelValue":e[1]||(e[1]=n=>s(o).password=n),fieldName:"password",fieldError:s(o).errors.get("password"),autocomplete:"current-password",showRules:!1,label:"auth.forms.current_password.label",help:"auth.forms.current_password.help"},null,8,["modelValue","fieldError"]),u("div",x,[u("p",null,[c(m(t.$t("auth.forms.forgot_your_password"))+" ",1),l(v,{id:"lnkResetPwd",to:{name:"password.request"},class:"is-link","aria-label":t.$t("auth.forms.reset_your_password")},{default:d(()=>[c(m(t.$t("auth.forms.request_password_reset")),1)]),_:1},8,["to","aria-label"])])]),l(F,{submitId:"btnRecover",isBusy:s(o).isBusy,isDisabled:s(o).isDisabled,caption:t.$t("commons.continue"),showCancelButton:!0,cancelLandingView:"login"},null,8,["isBusy","isDisabled","caption"])],40,W)]),l(k)]),_:1},8,["title","punchline"])}}};export{I as default};
|
|
@ -1 +0,0 @@
|
|||
import{P as V,b as B,u as R,Z as g,a2 as $,d as N,$ as C,e as a,f as D,k as S,p as d,h as u,D as q,i as s,m as l,j as c,t as m,I as A}from"./app-1b332c21.js";import{F as L}from"./Form-940b5f6c.js";/*! 2FAuth version 5.0.0 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const P=["onSubmit"],W={class:"field"},I={__name:"Recover",setup(x){const p=V("2fauth"),r=B(),f=R(),i=g(),h=$(p.prefix+"showWebauthnForm",!1),o=N(new L({email:i.query.email,password:"",token:i.query.token,revokeAll:!1}));function _(t){r.clear(),o.post("/webauthn/recover",{returnError:!0}).then(e=>{h.value=!1,f.push({name:"login"})}).catch(e=>{e.response.status===401?r.alert({text:A("auth.forms.authentication_failed"),duration:-1}):e.response.status===422?r.alert({text:e.response.data.message,duration:-1}):r.error(e)})}return C(()=>{r.clear()}),(t,e)=>{const w=a("FormCheckbox"),b=a("FormPasswordField"),v=a("RouterLink"),F=a("FormButtons"),k=a("VueFooter"),y=a("FormWrapper");return D(),S(y,{title:t.$t("auth.webauthn.account_recovery"),punchline:t.$t("auth.webauthn.recover_account_instructions")},{default:d(()=>[u("div",null,[u("form",{onSubmit:q(_,["prevent"]),onKeydown:e[2]||(e[2]=n=>s(o).onKeydown(n))},[l(w,{modelValue:s(o).revokeAll,"onUpdate:modelValue":e[0]||(e[0]=n=>s(o).revokeAll=n),fieldName:"revokeAll",label:"auth.webauthn.disable_all_security_devices",help:"auth.webauthn.disable_all_security_devices_help"},null,8,["modelValue"]),l(b,{modelValue:s(o).password,"onUpdate:modelValue":e[1]||(e[1]=n=>s(o).password=n),fieldName:"password",fieldError:s(o).errors.get("password"),autocomplete:"current-password",showRules:!1,label:"auth.forms.current_password.label",help:"auth.forms.current_password.help"},null,8,["modelValue","fieldError"]),u("div",W,[u("p",null,[c(m(t.$t("auth.forms.forgot_your_password"))+" ",1),l(v,{id:"lnkResetPwd",to:{name:"password.request"},class:"is-link","aria-label":t.$t("auth.forms.reset_your_password")},{default:d(()=>[c(m(t.$t("auth.forms.request_password_reset")),1)]),_:1},8,["to","aria-label"])])]),l(F,{submitId:"btnRecover",isBusy:s(o).isBusy,isDisabled:s(o).isDisabled,caption:t.$t("commons.continue"),showCancelButton:!0,cancelLandingView:"login"},null,8,["isBusy","isDisabled","caption"])],40,P)]),l(k)]),_:1},8,["title","punchline"])}}};export{I as default};
|
|
@ -1 +0,0 @@
|
|||
import{a as T,b as C,u as I,r as F,d as k,$ as K,e as l,f as u,g as _,i as t,k as V,p as c,m as n,h as i,j as p,t as d,D,I as U}from"./app-1b332c21.js";import{F as B}from"./Form-940b5f6c.js";import{w as W}from"./webauthnService-839b1903.js";/*! 2FAuth version 5.0.0 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const P={key:0,class:"field"},M={id:"lblDeviceRegistrationSuccess",class:"label mb-5"},j=["onSubmit"],A={key:1,class:"field is-grouped"},J={class:"control"},O={class:"control"},Y=["onSubmit"],q={class:"nav-links"},X={__name:"Register",setup(z){const b=T(),m=C(),R=I(),w=F(!1),f=F(null),s=k(new B({name:"",email:"",password:"",password_confirmation:""})),r=k(new B({name:""}));async function S(o){s.password_confirmation=s.password,s.post("/user").then(e=>{b.$patch({name:e.data.name,email:e.data.email,preferences:e.data.preferences,isAdmin:e.data.is_admin??!1}),b.applyTheme(),w.value=!0})}function N(){W.register().then(o=>{const e=JSON.parse(o.config.data);f.value=e.id}).catch(o=>{o.response.status===422?m.alert({text:o.response.data.message}):m.error(o)})}function E(o){r.patch("/webauthn/credentials/"+f.value+"/name").then(()=>{m.success({text:U("auth.webauthn.device_successfully_registered")}),R.push({name:"accounts"})})}return K(()=>{m.clear()}),(o,e)=>{const $=l("font-awesome-icon"),h=l("FormField"),g=l("FormButtons"),y=l("RouterLink"),v=l("FormWrapper"),x=l("FormPasswordField"),L=l("VueFooter");return u(),_("div",null,[t(w)?(u(),V(v,{key:0,title:"auth.authentication",punchline:"auth.webauthn.enhance_security_using_webauthn"},{default:c(()=>[t(f)?(u(),_("div",P,[i("label",M,[p(d(o.$t("auth.webauthn.device_successfully_registered"))+" ",1),n($,{icon:["fas","check"]})]),i("form",{onSubmit:D(E,["prevent"]),onKeydown:e[1]||(e[1]=a=>t(r).onKeydown(a))},[n(h,{modelValue:t(r).name,"onUpdate:modelValue":e[0]||(e[0]=a=>t(r).name=a),fieldName:"name",fieldError:t(r).errors.get("name"),inputType:"text",placeholder:"iPhone 12, TouchID, Yubikey 5C",label:"auth.forms.name_this_device"},null,8,["modelValue","fieldError"]),n(g,{isBusy:t(r).isBusy,isDisabled:t(r).isDisabled,caption:"commons.continue"},null,8,["isBusy","isDisabled"])],40,j)])):(u(),_("div",A,[i("div",J,[i("button",{type:"button",id:"btnRegisterNewDevice",onClick:e[2]||(e[2]=a=>N()),class:"button is-link"},d(o.$t("auth.webauthn.register_a_device")),1)]),i("div",O,[n(y,{id:"btnMaybeLater",to:{name:"accounts"},class:"button is-text"},{default:c(()=>[p(d(o.$t("auth.maybe_later")),1)]),_:1})])]))]),_:1})):(u(),V(v,{key:1,title:"auth.register",punchline:"auth.forms.register_punchline"},{default:c(()=>[i("form",{onSubmit:D(S,["prevent"]),onKeydown:e[6]||(e[6]=a=>t(s).onKeydown(a))},[n(h,{modelValue:t(s).name,"onUpdate:modelValue":e[3]||(e[3]=a=>t(s).name=a),fieldName:"name",fieldError:t(s).errors.get("name"),inputType:"text",label:"auth.forms.name",maxLength:255,autofocus:""},null,8,["modelValue","fieldError"]),n(h,{modelValue:t(s).email,"onUpdate:modelValue":e[4]||(e[4]=a=>t(s).email=a),fieldName:"email",fieldError:t(s).errors.get("email"),inputType:"email",label:"auth.forms.email",maxLength:255},null,8,["modelValue","fieldError"]),n(x,{modelValue:t(s).password,"onUpdate:modelValue":e[5]||(e[5]=a=>t(s).password=a),fieldName:"password",fieldError:t(s).errors.get("password"),showRules:!0,label:"auth.forms.password"},null,8,["modelValue","fieldError"]),n(g,{isBusy:t(s).isBusy,isDisabled:t(s).isDisabled,caption:"auth.register",submitId:"btnRegister"},null,8,["isBusy","isDisabled"])],40,Y),i("div",q,[i("p",null,[p(d(o.$t("auth.forms.already_register"))+" ",1),n(y,{id:"lnkSignIn",to:{name:"login"},class:"is-link"},{default:c(()=>[p(d(o.$t("auth.sign_in")),1)]),_:1})])])]),_:1})),n(L)])}}};export{X as default};
|
|
@ -0,0 +1 @@
|
|||
import{a as T,b as C,u as I,r as F,d as k,a0 as K,e as l,f as u,g as _,i as t,k as V,p as c,m as n,h as i,j as p,t as d,D,I as U}from"./app-2d89b28f.js";import{F as B}from"./Form-5283f7b6.js";import{w as W}from"./webauthnService-43ef310f.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const P={key:0,class:"field"},M={id:"lblDeviceRegistrationSuccess",class:"label mb-5"},j=["onSubmit"],A={key:1,class:"field is-grouped"},J={class:"control"},O={class:"control"},Y=["onSubmit"],q={class:"nav-links"},X={__name:"Register",setup(z){const b=T(),m=C(),R=I(),w=F(!1),f=F(null),s=k(new B({name:"",email:"",password:"",password_confirmation:""})),r=k(new B({name:""}));async function S(o){s.password_confirmation=s.password,s.post("/user").then(e=>{b.$patch({name:e.data.name,email:e.data.email,preferences:e.data.preferences,isAdmin:e.data.is_admin??!1}),b.applyTheme(),w.value=!0})}function N(){W.register().then(o=>{const e=JSON.parse(o.config.data);f.value=e.id}).catch(o=>{o.response.status===422?m.alert({text:o.response.data.message}):m.error(o)})}function E(o){r.patch("/webauthn/credentials/"+f.value+"/name").then(()=>{m.success({text:U("auth.webauthn.device_successfully_registered")}),R.push({name:"accounts"})})}return K(()=>{m.clear()}),(o,e)=>{const $=l("font-awesome-icon"),h=l("FormField"),g=l("FormButtons"),y=l("RouterLink"),v=l("FormWrapper"),x=l("FormPasswordField"),L=l("VueFooter");return u(),_("div",null,[t(w)?(u(),V(v,{key:0,title:"auth.authentication",punchline:"auth.webauthn.enhance_security_using_webauthn"},{default:c(()=>[t(f)?(u(),_("div",P,[i("label",M,[p(d(o.$t("auth.webauthn.device_successfully_registered"))+" ",1),n($,{icon:["fas","check"]})]),i("form",{onSubmit:D(E,["prevent"]),onKeydown:e[1]||(e[1]=a=>t(r).onKeydown(a))},[n(h,{modelValue:t(r).name,"onUpdate:modelValue":e[0]||(e[0]=a=>t(r).name=a),fieldName:"name",fieldError:t(r).errors.get("name"),inputType:"text",placeholder:"iPhone 12, TouchID, Yubikey 5C",label:"auth.forms.name_this_device"},null,8,["modelValue","fieldError"]),n(g,{isBusy:t(r).isBusy,isDisabled:t(r).isDisabled,caption:"commons.continue"},null,8,["isBusy","isDisabled"])],40,j)])):(u(),_("div",A,[i("div",J,[i("button",{type:"button",id:"btnRegisterNewDevice",onClick:e[2]||(e[2]=a=>N()),class:"button is-link"},d(o.$t("auth.webauthn.register_a_device")),1)]),i("div",O,[n(y,{id:"btnMaybeLater",to:{name:"accounts"},class:"button is-text"},{default:c(()=>[p(d(o.$t("auth.maybe_later")),1)]),_:1})])]))]),_:1})):(u(),V(v,{key:1,title:"auth.register",punchline:"auth.forms.register_punchline"},{default:c(()=>[i("form",{onSubmit:D(S,["prevent"]),onKeydown:e[6]||(e[6]=a=>t(s).onKeydown(a))},[n(h,{modelValue:t(s).name,"onUpdate:modelValue":e[3]||(e[3]=a=>t(s).name=a),fieldName:"name",fieldError:t(s).errors.get("name"),inputType:"text",label:"auth.forms.name",maxLength:255,autofocus:""},null,8,["modelValue","fieldError"]),n(h,{modelValue:t(s).email,"onUpdate:modelValue":e[4]||(e[4]=a=>t(s).email=a),fieldName:"email",fieldError:t(s).errors.get("email"),inputType:"email",label:"auth.forms.email",maxLength:255},null,8,["modelValue","fieldError"]),n(x,{modelValue:t(s).password,"onUpdate:modelValue":e[5]||(e[5]=a=>t(s).password=a),fieldName:"password",fieldError:t(s).errors.get("password"),showRules:!0,label:"auth.forms.password"},null,8,["modelValue","fieldError"]),n(g,{isBusy:t(s).isBusy,isDisabled:t(s).isDisabled,caption:"auth.register",submitId:"btnRegister"},null,8,["isBusy","isDisabled"])],40,Y),i("div",q,[i("p",null,[p(d(o.$t("auth.forms.already_register"))+" ",1),n(y,{id:"lnkSignIn",to:{name:"login"},class:"is-link"},{default:c(()=>[p(d(o.$t("auth.sign_in")),1)]),_:1})])])]),_:1})),n(L)])}}};export{X as default};
|
|
@ -1 +0,0 @@
|
|||
import{b as f,Z as h,d as _,$ as w,e as r,f as F,k as b,p as y,h as B,m as i,i as s,D as V}from"./app-1b332c21.js";import{F as v}from"./Form-940b5f6c.js";/*! 2FAuth version 5.0.0 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const R=["onSubmit"],S={__name:"RequestReset",setup(k){const o=f(),n=h().name=="webauthn.lost",t=_(new v({email:""}));function l(a){o.clear(),t.post(n?"/webauthn/lost":"/user/password/lost",{returnError:!0}).then(e=>{o.success({text:e.data.message,duration:-1})}).catch(e=>{e.response.data.requestFailed?o.alert({text:e.response.data.requestFailed,duration:-1}):e.response.status!==422&&o.error(e)})}return w(()=>{o.clear()}),(a,e)=>{const m=r("FormField"),c=r("FormButtons"),d=r("VueFooter"),p=r("FormWrapper");return F(),b(p,{title:a.$t(n?"auth.webauthn.account_recovery":"auth.forms.reset_password"),punchline:a.$t(n?"auth.webauthn.recovery_punchline":"auth.forms.reset_punchline")},{default:y(()=>[B("form",{onSubmit:V(l,["prevent"]),onKeydown:e[1]||(e[1]=u=>s(t).onKeydown(u))},[i(m,{modelValue:s(t).email,"onUpdate:modelValue":e[0]||(e[0]=u=>s(t).email=u),fieldName:"email",fieldError:s(t).errors.get("email"),label:"auth.forms.email",autofocus:""},null,8,["modelValue","fieldError"]),i(c,{submitId:"btnSendResetPwd",isBusy:s(t).isBusy,caption:a.$t(n?"auth.webauthn.send_recovery_link":"auth.forms.send_password_reset_link"),showCancelButton:!0,cancelLandingView:"login"},null,8,["isBusy","caption"])],40,R),i(d)]),_:1},8,["title","punchline"])}}};export{S as default};
|
|
@ -0,0 +1 @@
|
|||
import{b as f,_,d as h,a0 as w,e as r,f as F,k as b,p as y,h as B,m as i,i as s,D as V}from"./app-2d89b28f.js";import{F as v}from"./Form-5283f7b6.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const R=["onSubmit"],S={__name:"RequestReset",setup(k){const o=f(),n=_().name=="webauthn.lost",t=h(new v({email:""}));function l(a){o.clear(),t.post(n?"/webauthn/lost":"/user/password/lost",{returnError:!0}).then(e=>{o.success({text:e.data.message,duration:-1})}).catch(e=>{e.response.data.requestFailed?o.alert({text:e.response.data.requestFailed,duration:-1}):e.response.status!==422&&o.error(e)})}return w(()=>{o.clear()}),(a,e)=>{const m=r("FormField"),c=r("FormButtons"),d=r("VueFooter"),p=r("FormWrapper");return F(),b(p,{title:a.$t(n?"auth.webauthn.account_recovery":"auth.forms.reset_password"),punchline:a.$t(n?"auth.webauthn.recovery_punchline":"auth.forms.reset_punchline")},{default:y(()=>[B("form",{onSubmit:V(l,["prevent"]),onKeydown:e[1]||(e[1]=u=>s(t).onKeydown(u))},[i(m,{modelValue:s(t).email,"onUpdate:modelValue":e[0]||(e[0]=u=>s(t).email=u),fieldName:"email",fieldError:s(t).errors.get("email"),label:"auth.forms.email",autofocus:""},null,8,["modelValue","fieldError"]),i(c,{submitId:"btnSendResetPwd",isBusy:s(t).isBusy,caption:a.$t(n?"auth.webauthn.send_recovery_link":"auth.forms.send_password_reset_link"),showCancelButton:!0,cancelLandingView:"login"},null,8,["isBusy","caption"])],40,R),i(d)]),_:1},8,["title","punchline"])}}};export{S as default};
|
|
@ -1 +0,0 @@
|
|||
import{b as g,u as B,Z as h,r as R,d as E,$ as N,e as s,f as i,k as l,p as c,h as v,m as d,i as t,l as m,j as C,t as P,D as L}from"./app-1b332c21.js";import{F as S}from"./Form-940b5f6c.js";/*! 2FAuth version 5.0.0 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const $=["onSubmit"],K={__name:"Reset",setup(x){const n=g();B();const p=h(),u=R(!0),e=E(new S({email:p.query.email,password:"",password_confirmation:"",token:p.query.token}));function f(a){e.password_confirmation=e.password,e.post("/user/password/reset",{returnError:!0}).then(o=>{e.password="",e.password_confirmation="",u.value=!1,n.success({text:o.data.message,duration:-1})}).catch(o=>{o.response.data.resetFailed?n.alert({text:o.response.data.resetFailed,duration:-1}):o.response.status!==422&&n.error(o)})}return N(()=>{n.clear()}),(a,o)=>{const w=s("FormField"),_=s("FormPasswordField"),F=s("FieldError"),k=s("FormButtons"),y=s("RouterLink"),V=s("VueFooter"),b=s("FormWrapper");return i(),l(b,{title:a.$t("auth.forms.new_password")},{default:c(()=>[v("form",{onSubmit:L(f,["prevent"]),onKeydown:o[2]||(o[2]=r=>t(e).onKeydown(r))},[d(w,{modelValue:t(e).email,"onUpdate:modelValue":o[0]||(o[0]=r=>t(e).email=r),isDisabled:!0,fieldName:"email",fieldError:t(e).errors.get("email"),label:"auth.forms.email",autofocus:""},null,8,["modelValue","fieldError"]),d(_,{modelValue:t(e).password,"onUpdate:modelValue":o[1]||(o[1]=r=>t(e).password=r),fieldName:"password",fieldError:t(e).errors.get("password"),autocomplete:"new-password",showRules:!0,label:"auth.forms.new_password"},null,8,["modelValue","fieldError"]),t(e).errors.get("token")!=null?(i(),l(F,{key:0,error:t(e).errors.get("token"),field:t(e).token},null,8,["error","field"])):m("",!0),t(u)?(i(),l(k,{key:1,submitId:"btnResetPwd",isBusy:t(e).isBusy,caption:a.$t("auth.forms.change_password"),showCancelButton:!0,cancelLandingView:"login"},null,8,["isBusy","caption"])):m("",!0),t(u)?m("",!0):(i(),l(y,{key:2,id:"btnContinue",to:{name:"accounts"},class:"button is-link"},{default:c(()=>[C(P(a.$t("commons.continue")),1)]),_:1}))],40,$),d(V)]),_:1},8,["title"])}}};export{K as default};
|
|
@ -0,0 +1 @@
|
|||
import{b as g,u as B,_ as h,r as R,d as E,a0 as N,e as s,f as i,k as l,p as c,h as v,m as d,i as t,l as m,j as C,t as P,D as L}from"./app-2d89b28f.js";import{F as S}from"./Form-5283f7b6.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const x=["onSubmit"],K={__name:"Reset",setup(D){const a=g();B();const p=h(),u=R(!0),e=E(new S({email:p.query.email,password:"",password_confirmation:"",token:p.query.token}));function f(n){e.password_confirmation=e.password,e.post("/user/password/reset",{returnError:!0}).then(o=>{e.password="",e.password_confirmation="",u.value=!1,a.success({text:o.data.message,duration:-1})}).catch(o=>{o.response.data.resetFailed?a.alert({text:o.response.data.resetFailed,duration:-1}):o.response.status!==422&&a.error(o)})}return N(()=>{a.clear()}),(n,o)=>{const w=s("FormField"),_=s("FormPasswordField"),F=s("FieldError"),k=s("FormButtons"),y=s("RouterLink"),V=s("VueFooter"),b=s("FormWrapper");return i(),l(b,{title:n.$t("auth.forms.new_password")},{default:c(()=>[v("form",{onSubmit:L(f,["prevent"]),onKeydown:o[2]||(o[2]=r=>t(e).onKeydown(r))},[d(w,{modelValue:t(e).email,"onUpdate:modelValue":o[0]||(o[0]=r=>t(e).email=r),isDisabled:!0,fieldName:"email",fieldError:t(e).errors.get("email"),label:"auth.forms.email",autofocus:""},null,8,["modelValue","fieldError"]),d(_,{modelValue:t(e).password,"onUpdate:modelValue":o[1]||(o[1]=r=>t(e).password=r),fieldName:"password",fieldError:t(e).errors.get("password"),autocomplete:"new-password",showRules:!0,label:"auth.forms.new_password"},null,8,["modelValue","fieldError"]),t(e).errors.get("token")!=null?(i(),l(F,{key:0,error:t(e).errors.get("token"),field:t(e).token},null,8,["error","field"])):m("",!0),t(u)?(i(),l(k,{key:1,submitId:"btnResetPwd",isBusy:t(e).isBusy,caption:n.$t("auth.forms.change_password"),showCancelButton:!0,cancelLandingView:"login"},null,8,["isBusy","caption"])):m("",!0),t(u)?m("",!0):(i(),l(y,{key:2,id:"btnContinue",to:{name:"accounts"},class:"button is-link"},{default:c(()=>[C(P(n.$t("commons.continue")),1)]),_:1}))],40,x),d(V)]),_:1},8,["title"])}}};export{K as default};
|
|
@ -0,0 +1 @@
|
|||
import{r as d,o as u,s as h,e as m,f as n,g as c,h as a,n as k,k as p}from"./app-2d89b28f.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const y={role:"search",class:"field"},f={class:"control has-icons-right"},v=["aria-label","title","placeholder","value"],g={class:"icon is-small is-right"},w=["title"],b={__name:"SearchBox",props:{keyword:String,hasNoBackground:{type:Boolean,default:!1},placeholder:String},setup(t){const s=d(null);u(()=>{document.addEventListener("keydown",r)}),h(()=>{document.removeEventListener("keydown",r)});function r(e){var o;e.key==="f"&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),(o=s.value)==null||o.focus())}return(e,o)=>{const i=m("FontAwesomeIcon");return n(),c("div",y,[a("div",f,[a("input",{ref_key:"searchInput",ref:s,id:"txtSearch",type:"search",tabindex:"1","aria-label":e.$t("commons.search"),title:e.$t("commons.search"),placeholder:t.placeholder,class:k(["input is-rounded is-search",{"has-no-background":t.hasNoBackground}]),value:t.keyword,onKeyup:o[0]||(o[0]=l=>e.$emit("update:keyword",l.target.value))},null,42,v),a("span",g,[t.keyword!=""?(n(),c("button",{key:0,id:"btnClearSearch",tabindex:"1",title:e.$t("commons.clear_search"),class:"clear-selection delete",onClick:o[1]||(o[1]=l=>e.$emit("update:keyword",""))},null,8,w)):(n(),p(i,{key:1,icon:["fas","search"]}))])])])}}};export{b as _};
|
|
@ -1 +1 @@
|
|||
import{r as m,e as n,f as t,g as s,m as a,p as i,h as o,F as v,G as _,i as h,n as g,j as w,t as k}from"./app-1b332c21.js";/*! 2FAuth version 5.0.0 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const f={class:"options-header"},b={class:"tabs is-centered is-fullwidth"},R={__name:"SettingTabs",props:{activeTab:{type:String,default:""}},setup(r){const c=r,l=m([{name:"settings.options",view:"settings.options",id:"lnkTabOptions"},{name:"settings.account",view:"settings.account",id:"lnkTabAccount"},{name:"settings.oauth",view:"settings.oauth.tokens",id:"lnkTabOAuth"},{name:"settings.webauthn",view:"settings.webauthn.devices",id:"lnkTabWebauthn"}]);return(u,T)=>{const d=n("RouterLink"),p=n("ResponsiveWidthWrapper");return t(),s("div",f,[a(p,null,{default:i(()=>[o("div",b,[o("ul",null,[(t(!0),s(v,null,_(h(l),e=>(t(),s("li",{key:e.view,class:g({"is-active":e.view===c.activeTab})},[a(d,{id:e.id,to:{name:e.view}},{default:i(()=>[w(k(u.$t(e.name)),1)]),_:2},1032,["id","to"])],2))),128))])])]),_:1})])}}};export{R as _};
|
||||
import{r as m,e as n,f as t,g as s,m as a,p as i,h as o,F as v,G as _,i as h,n as g,j as w,t as k}from"./app-2d89b28f.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const f={class:"options-header"},b={class:"tabs is-centered is-fullwidth"},R={__name:"SettingTabs",props:{activeTab:{type:String,default:""}},setup(r){const c=r,l=m([{name:"settings.options",view:"settings.options",id:"lnkTabOptions"},{name:"settings.account",view:"settings.account",id:"lnkTabAccount"},{name:"settings.oauth",view:"settings.oauth.tokens",id:"lnkTabOAuth"},{name:"settings.webauthn",view:"settings.webauthn.devices",id:"lnkTabWebauthn"}]);return(u,T)=>{const d=n("RouterLink"),p=n("ResponsiveWidthWrapper");return t(),s("div",f,[a(p,null,{default:i(()=>[o("div",b,[o("ul",null,[(t(!0),s(v,null,_(h(l),e=>(t(),s("li",{key:e.view,class:g({"is-active":e.view===c.activeTab})},[a(d,{id:e.id,to:{name:e.view}},{default:i(()=>[w(k(u.$t(e.name)),1)]),_:2},1032,["id","to"])],2))),128))])])]),_:1})])}}};export{R as _};
|
|
@ -1 +0,0 @@
|
|||
.spinner-container[data-v-56b21d53],.spinner-overlay-container[data-v-56b21d53]{text-align:center;z-index:100000;position:absolute;top:0;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center}.spinner-container[data-v-56b21d53],.spinner-overlay-container[data-v-56b21d53]{top:25%;height:50%}.spinner[data-v-56b21d53]{display:block}
|
|
@ -0,0 +1 @@
|
|||
.spinner-container[data-v-247a4fa7],.spinner-overlay-container[data-v-247a4fa7]{text-align:center;z-index:100000;position:absolute;top:0;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center}.spinner-container[data-v-247a4fa7],.spinner-overlay-container[data-v-247a4fa7]{top:25%;height:50%}.spinner[data-v-247a4fa7]{display:block}
|
|
@ -1 +1 @@
|
|||
import{a4 as p,e as l,f as n,g as t,h as e,m as o,t as c,l as r,k as d}from"./app-1b332c21.js";/*! 2FAuth version 5.0.0 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const _={key:0},m={key:0,class:"spinner-container"},y={class:"spinner-wrapper"},u={id:"icnSpinnerFull",class:"is-size-1 spinner"},f={key:1,class:"spinner-overlay-container"},h={class:"spinner-wrapper"},v={id:"icnSpinnerFull",class:"is-size-1 spinner"},g={key:3,class:"has-text-centered mt-6"},S={id:"icnSpinner",class:"is-size-4"},k={__name:"Spinner",props:{isVisible:Boolean,type:{type:String,default:"inline"},message:{type:String,default:"commons.generating_otp"}},setup(s){return(a,w)=>{const i=l("FontAwesomeIcon");return s.isVisible?(n(),t("div",_,[s.type=="fullscreen"?(n(),t("div",m,[e("div",y,[e("span",u,[o(i,{icon:["fas","spinner"],spin:""})]),e("span",null,c(a.$t(s.message)),1)])])):r("",!0),s.type=="fullscreen-overlay"?(n(),t("div",f,[e("div",h,[e("span",v,[o(i,{icon:["fas","spinner"],spin:""})]),e("span",null,c(a.$t(s.message)),1)])])):s.type=="raw"?(n(),d(i,{key:2,icon:["fas","spinner"],spin:""})):(n(),t("div",g,[e("span",S,[o(i,{icon:["fas","spinner"],spin:""})])]))])):r("",!0)}}},V=p(k,[["__scopeId","data-v-56b21d53"]]);export{V as S};
|
||||
import{a5 as p,e as l,f as n,g as t,h as e,m as a,t as c,l as r,k as d}from"./app-2d89b28f.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const _={key:0},m={key:0,class:"spinner-container"},y={class:"spinner-wrapper"},u={id:"icnSpinnerFull",class:"is-size-1 spinner"},f={key:1,class:"spinner-overlay-container"},h={class:"spinner-wrapper"},v={id:"icnSpinnerFull",class:"is-size-1 spinner"},g={key:3,class:"has-text-centered mt-6"},S={id:"icnSpinner",class:"is-size-4"},k={__name:"Spinner",props:{isVisible:Boolean,type:{type:String,default:"inline"},message:{type:String,default:"commons.generating_otp"}},setup(s){return(o,w)=>{const i=l("FontAwesomeIcon");return s.isVisible?(n(),t("div",_,[s.type=="fullscreen"?(n(),t("div",m,[e("div",y,[e("span",u,[a(i,{icon:["fas","spinner"],spin:""})]),e("span",null,c(o.$t(s.message)),1)])])):r("",!0),s.type=="fullscreen-overlay"?(n(),t("div",f,[e("div",h,[e("span",v,[a(i,{icon:["fas","spinner"],spin:""})]),e("span",null,c(o.$t(s.message)),1)])])):s.type=="raw"?(n(),d(i,{key:2,icon:["fas","spinner"],spin:""})):(n(),t("div",g,[e("span",S,[a(i,{icon:["fas","spinner"],spin:""})])]))])):r("",!0)}}},V=p(k,[["__scopeId","data-v-247a4fa7"]]);export{V as S};
|
|
@ -1 +1 @@
|
|||
import{u as U,a as V,b as A,c as E,r as w,d as N,o as R,e as d,f as a,g as f,h as t,n as C,i as o,j as r,t as s,w as g,k as B,l as b,m as p,p as m,U as S}from"./app-1b332c21.js";import{F as K}from"./Form-940b5f6c.js";import{u as M}from"./bus-84126a4e.js";/*! 2FAuth version 5.0.0 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const Q={class:"container has-text-centered"},T={class:"columns quick-uploader"},D=t("br",null,null,-1),j={class:"column is-full quick-uploader-button"},z={class:"quick-uploader-centerer"},G={class:"column is-full"},H={key:0,class:"block has-text-link"},J={class:"block has-text-link"},O={class:"block has-text-link"},Z={__name:"Start",setup(P){const k=U(),i=V(),h=M(),$=A(),v=E(),_=w(null),c=w(null),u=N(new K({qrcode:null,inputFormat:"fileUpload"}));function y(){u.clear(),u.qrcode=_.value.files[0],u.upload("/api/v1/qrcode/decode",{returnError:!0}).then(e=>{e.data.data.slice(0,33).toLowerCase()==="otpauth-migration://offline?data="?(h.migrationUri=e.data.data,k.push({name:"importAccounts"})):(h.decodedUri=e.data.data,k.push({name:"createAccount"}))}).catch(e=>{e.response.status!==422&&$.alert({text:e.response.data.message})})}function x(){k.push({name:"capture"})}return R(()=>{i.preferences.useDirectCapture&&i.preferences.defaultCaptureMode==="upload"&&c.value.click()}),(e,n)=>{const F=d("FieldError"),q=d("RouterLink"),I=d("ButtonBackCloseCancel"),L=d("VueFooter");return a(),f("div",Q,[t("div",T,[t("div",{class:C(["column is-full quick-uploader-header",{"is-invisible":o(v).count!==0}])},[r(s(e.$t("twofaccounts.no_account_here")),1),D,r(" "+s(e.$t("twofaccounts.add_first_account")),1)],2),t("div",j,[t("div",z,[o(i).preferences.useBasicQrcodeReader?(a(),f("label",{key:0,role:"button",tabindex:"0",class:"button is-link is-medium is-rounded is-main",ref_key:"qrcodeInputLabel",ref:c,onKeyup:n[0]||(n[0]=g(l=>o(c).click(),["enter"]))},[t("input",{"aria-hidden":"true",tabindex:"-1",class:"file-input",type:"file",accept:"image/*",onChange:y,ref_key:"qrcodeInput",ref:_},null,544),r(" "+s(e.$t("twofaccounts.forms.upload_qrcode")),1)],544)):(a(),f("button",{key:1,class:"button is-link is-medium is-rounded is-main",onClick:n[1]||(n[1]=l=>x())},s(e.$t("twofaccounts.forms.scan_qrcode")),1))]),o(u).errors.hasAny("qrcode")?(a(),B(F,{key:0,error:o(u).errors.get("qrcode"),field:"qrcode"},null,8,["error"])):b("",!0)]),t("div",G,[p(o(S),null,{default:m(({mode:l})=>[t("div",{class:C(["block",l=="dark"?"has-text-light":"has-text-grey-dark"])},s(e.$t("twofaccounts.forms.alternative_methods")),3)]),_:1}),o(i).preferences.useBasicQrcodeReader?b("",!0):(a(),f("div",H,[t("label",{role:"button",tabindex:"0",class:"button is-link is-outlined is-rounded",ref_key:"qrcodeInputLabel",ref:c,onKeyup:n[2]||(n[2]=g(l=>o(c).click(),["enter"]))},[t("input",{"aria-hidden":"true",tabindex:"-1",class:"file-input",type:"file",accept:"image/*",onChange:y,ref_key:"qrcodeInput",ref:_},null,544),r(" "+s(e.$t("twofaccounts.forms.upload_qrcode")),1)],544)])),t("div",J,[p(q,{class:"button is-link is-outlined is-rounded",to:{name:"createAccount"}},{default:m(()=>[r(s(e.$t("twofaccounts.forms.use_advanced_form")),1)]),_:1})]),t("div",O,[p(q,{id:"btnImport",class:"button is-link is-outlined is-rounded",to:{name:"importAccounts"}},{default:m(()=>[r(s(e.$t("twofaccounts.import.import")),1)]),_:1})])])]),p(L,{showButtons:!0},{default:m(()=>[o(v).isEmpty?b("",!0):(a(),B(I,{key:0,returnTo:{name:"accounts"},action:"back"}))]),_:1})])}}};export{Z as default};
|
||||
import{u as U,a as V,b as A,c as E,r as w,d as N,o as R,e as d,f as a,g as f,h as t,n as C,i as o,j as r,t as s,w as g,k as B,l as b,m as p,p as m,U as S}from"./app-2d89b28f.js";import{F as K}from"./Form-5283f7b6.js";import{u as M}from"./bus-7802a020.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const Q={class:"container has-text-centered"},T={class:"columns quick-uploader"},D=t("br",null,null,-1),j={class:"column is-full quick-uploader-button"},z={class:"quick-uploader-centerer"},G={class:"column is-full"},H={key:0,class:"block has-text-link"},J={class:"block has-text-link"},O={class:"block has-text-link"},Z={__name:"Start",setup(P){const k=U(),i=V(),h=M(),$=A(),v=E(),_=w(null),c=w(null),u=N(new K({qrcode:null,inputFormat:"fileUpload"}));function y(){u.clear(),u.qrcode=_.value.files[0],u.upload("/api/v1/qrcode/decode",{returnError:!0}).then(e=>{e.data.data.slice(0,33).toLowerCase()==="otpauth-migration://offline?data="?(h.migrationUri=e.data.data,k.push({name:"importAccounts"})):(h.decodedUri=e.data.data,k.push({name:"createAccount"}))}).catch(e=>{e.response.status!==422&&$.alert({text:e.response.data.message})})}function x(){k.push({name:"capture"})}return R(()=>{i.preferences.useDirectCapture&&i.preferences.defaultCaptureMode==="upload"&&c.value.click()}),(e,n)=>{const F=d("FieldError"),q=d("RouterLink"),I=d("ButtonBackCloseCancel"),L=d("VueFooter");return a(),f("div",Q,[t("div",T,[t("div",{class:C(["column is-full quick-uploader-header",{"is-invisible":o(v).count!==0}])},[r(s(e.$t("twofaccounts.no_account_here")),1),D,r(" "+s(e.$t("twofaccounts.add_first_account")),1)],2),t("div",j,[t("div",z,[o(i).preferences.useBasicQrcodeReader?(a(),f("label",{key:0,role:"button",tabindex:"0",class:"button is-link is-medium is-rounded is-main",ref_key:"qrcodeInputLabel",ref:c,onKeyup:n[0]||(n[0]=g(l=>o(c).click(),["enter"]))},[t("input",{"aria-hidden":"true",tabindex:"-1",class:"file-input",type:"file",accept:"image/*",onChange:y,ref_key:"qrcodeInput",ref:_},null,544),r(" "+s(e.$t("twofaccounts.forms.upload_qrcode")),1)],544)):(a(),f("button",{key:1,class:"button is-link is-medium is-rounded is-main",onClick:n[1]||(n[1]=l=>x())},s(e.$t("twofaccounts.forms.scan_qrcode")),1))]),o(u).errors.hasAny("qrcode")?(a(),B(F,{key:0,error:o(u).errors.get("qrcode"),field:"qrcode"},null,8,["error"])):b("",!0)]),t("div",G,[p(o(S),null,{default:m(({mode:l})=>[t("div",{class:C(["block",l=="dark"?"has-text-light":"has-text-grey-dark"])},s(e.$t("twofaccounts.forms.alternative_methods")),3)]),_:1}),o(i).preferences.useBasicQrcodeReader?b("",!0):(a(),f("div",H,[t("label",{role:"button",tabindex:"0",class:"button is-link is-outlined is-rounded",ref_key:"qrcodeInputLabel",ref:c,onKeyup:n[2]||(n[2]=g(l=>o(c).click(),["enter"]))},[t("input",{"aria-hidden":"true",tabindex:"-1",class:"file-input",type:"file",accept:"image/*",onChange:y,ref_key:"qrcodeInput",ref:_},null,544),r(" "+s(e.$t("twofaccounts.forms.upload_qrcode")),1)],544)])),t("div",J,[p(q,{class:"button is-link is-outlined is-rounded",to:{name:"createAccount"}},{default:m(()=>[r(s(e.$t("twofaccounts.forms.use_advanced_form")),1)]),_:1})]),t("div",O,[p(q,{id:"btnImport",class:"button is-link is-outlined is-rounded",to:{name:"importAccounts"}},{default:m(()=>[r(s(e.$t("twofaccounts.import.import")),1)]),_:1})])])]),p(L,{showButtons:!0},{default:m(()=>[o(v).isEmpty?b("",!0):(a(),B(I,{key:0,returnTo:{name:"accounts"},action:"back"}))]),_:1})])}}};export{Z as default};
|
|
@ -0,0 +1 @@
|
|||
import{_ as j}from"./AdminTabs-7af3858b.js";import{Q as N,b as S,a2 as T,r as k,v as $,o as I,J as E,a0 as R,e as p,f as u,g as m,m as n,h as t,p as h,t as i,j as B,i as r,E as O,F as W,G as M,U as F,n as y,l as A}from"./app-2d89b28f.js";import{S as Z}from"./Spinner-aea2b665.js";import{_ as q}from"./SearchBox-cb8625ff.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const D={class:"options-tabs"},G={class:"title is-4 has-text-grey-light"},J={class:"is-size-7-mobile"},Q={class:"mb-6 mt-3"},H={class:"columns"},K={class:"column pb-0"},P={class:"level is-mobile mb-0"},X={class:"level-item has-text-centered is-justify-content-end"},Y={class:"subtitle is-7"},ee={class:"level-item has-text-centered is-justify-content-start"},se={class:"buttons"},te={key:0},ae={class:"has-ellipsis"},oe={class:"is-block has-ellipsis is-family-primary is-size-6 is-size-7-mobile has-text-grey"},ne={class:"tags mt-2"},ie={class:"ml-3"},le={key:1,class:"mt-4 pl-3"},he={__name:"Users",setup(re){const x=N("2fauth"),w=S(),z=T(x.prefix+"returnTo","accounts"),f=k([]),a=k(""),g=k(!1),C=$(()=>f.value.filter(e=>{let s=e.name.toLowerCase().includes(d.value.keywords)||e.email.toLowerCase().includes(d.value.keywords);return d.value.admin!=null&&(s=s&&e.is_admin==d.value.admin),d.value.oauth!=null&&(s=s&&e.oauth_provider==d.value.oauth),s})),d=$(()=>{const e={admin:void 0,oauth:void 0,keywords:a.value.toLowerCase()},s=a.value.toLowerCase().split(" "),c=/admin:([01])/,_=/oauth:([a-zA-Z0-9])/;return s.forEach(l=>{l.match(c)&&(e.admin=parseInt(l.replace(c,"$1")),e.keywords=e.keywords.replace(l,"").trim()),l.match(_)&&(e.oauth=l.replace(_,"$1"),e.keywords=e.keywords.replace(l,"").trim())}),e});I(()=>{L()});function b(e){const s=/admin:([01])/,c=/oauth:([a-zA-Z0-9]*)/;a.value.match(s)&&e.match(s)?a.value=a.value.replace(s,e):d.value.oauth!=null&&e.match(c)?a.value=a.value.replace(c,e):a.value=a.value?a.value+" "+e:e}function L(){g.value=!0,E.getAll({returnError:!0}).then(e=>{f.value=e.data}).catch(e=>{w.error(e)}).finally(()=>{g.value=!1})}return R(e=>{e.name.startsWith("admin.")||w.clear()}),(e,s)=>{const c=p("FontAwesomeIcon"),_=p("RouterLink"),l=p("ButtonBackCloseCancel"),U=p("VueFooter"),V=p("FormWrapper");return u(),m("div",null,[n(j,{activeTab:"admin.users"}),t("div",D,[n(V,null,{default:h(()=>[t("h4",G,i(e.$t("admin.users")),1),t("div",J,i(e.$t("admin.users_legend")),1),t("div",Q,[n(_,{class:"is-link mt-5",to:{name:"admin.createUser"}},{default:h(()=>[n(c,{icon:["fas","plus-circle"]}),B(" "+i(e.$t("admin.create_new_user")),1)]),_:1},8,["to"])]),t("div",H,[t("div",K,[n(q,{keyword:r(a),"onUpdate:keyword":s[0]||(s[0]=o=>O(a)?a.value=o:null),hasNoBackground:!0,placeholder:e.$t("admin.search_user_placeholder")},null,8,["keyword","placeholder"])])]),t("div",P,[t("div",X,[t("p",Y,i(e.$t("admin.quick_filters_colons")),1)]),t("div",ee,[t("div",se,[t("button",{class:"button is-small is-ghost p-0",onClick:s[1]||(s[1]=o=>b("admin:1"))},"admin"),t("button",{class:"button is-small is-ghost p-0",onClick:s[2]||(s[2]=o=>b("oauth:github"))},"github"),t("button",{class:"button is-small is-ghost p-0",onClick:s[3]||(s[3]=o=>b("oauth:openid"))},"openId")])])]),r(C).length>0?(u(),m("div",te,[(u(!0),m(W,null,M(r(C),o=>(u(),m("div",{key:o.id,class:"list-item is-size-5 is-size-6-mobile is-flex is-justify-content-space-between"},[t("div",ae,[t("span",null,i(o.name),1),t("span",oe,i(o.email),1),n(r(F),null,{default:h(({mode:v})=>[t("div",ne,[o.is_admin?(u(),m("span",{key:0,class:y(["tag is-rounded has-text-warning-dark",v=="dark"?"has-background-black-bis":"has-background-grey-lighter"])},"admin",2)):A("",!0),o.oauth_provider?(u(),m("span",{key:1,class:y(["tag is-rounded has-text-grey",v=="dark"?"has-background-black-bis":"has-background-grey-lighter"])},"oauth: "+i(o.oauth_provider),3)):A("",!0)])]),_:2},1024)]),t("div",ie,[n(r(F),null,{default:h(({mode:v})=>[n(_,{to:{name:"admin.manageUser",params:{userId:o.id}},class:y(["button is-small has-normal-radius is-pulled-right",{"is-dark":v=="dark"}]),title:e.$t("commons.manage")},{default:h(()=>[B(i(e.$t("commons.manage")),1)]),_:2},1032,["to","class","title"])]),_:2},1024)])]))),128))])):(u(),m("div",le,i(e.$t("commons.no_result")),1)),n(Z,{isVisible:r(g)&&r(f).length===0},null,8,["isVisible"]),n(U,{showButtons:!0},{default:h(()=>[n(l,{returnTo:{name:r(z)},action:"close"},null,8,["returnTo"])]),_:1})]),_:1})])])}}};export{he as default};
|
|
@ -1 +0,0 @@
|
|||
import{P as z,a as U,b as L,u as M,a2 as A,r as b,o as D,x as I,$ as K,e as h,f as d,g as _,m as o,h as s,p as v,i as t,l as y,t as i,w as R,j as k,F as j,G as E,U as H,n as P,I as r}from"./app-1b332c21.js";import{_ as G}from"./SettingTabs-52d14fa3.js";import{u as g}from"./userService-5f2b5050.js";import{w as J}from"./webauthnService-839b1903.js";import{S as q}from"./Spinner-b3cbad3a.js";/*! 2FAuth version 5.0.0 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const Q={class:"options-tabs"},X=["innerHTML"],Y={class:"title is-4 has-text-grey-light"},Z={class:"is-size-7-mobile"},ee={class:"mt-3"},te=["onKeyup"],se={key:1,class:"field"},ne=["onClick","title"],ae={class:"mt-2 is-size-7 is-pulled-right"},oe={class:"title is-4 pt-6 has-text-grey-light"},ie={class:"field"},pe={__name:"WebAuthn",setup(re){const C=z("2fauth"),l=U(),n=L(),W=M(),$=A(C.prefix+"returnTo","accounts"),a=b([]),f=b(!1),p=b(!1);D(()=>{V()}),I(()=>l.preferences.useWebauthnOnly,()=>{g.updatePreference("useWebauthnOnly",l.preferences.useWebauthnOnly).then(e=>{n.success({text:r("settings.forms.setting_saved")})})});function w(){if(p==!0)return n.warn({text:r("errors.unsupported_with_reverseproxy")}),!1;J.register().then(e=>{W.push({name:"settings.webauthn.editCredential",params:{credentialId:JSON.parse(e.config.data).id}})}).catch(e=>{var u;"webauthn"in e?e.name=="is-warning"?n.warn({text:r(e.message)}):n.alert({text:r(e.message)}):((u=e.response)==null?void 0:u.status)===422?n.alert({text:e.response.data.message}):n.error(e)})}function x(e){confirm(r("auth.confirm.revoke_device"))&&g.revokeWebauthnDevice(e).then(u=>{a.value=a.value.filter(m=>m.id!==e),a.value.length==0&&(l.preferences.useWebauthnOnly=!1),n.success({text:r("auth.webauthn.device_revoked")})})}function F(e){return e.alias?e.alias:r("auth.webauthn.my_device")+" (#"+e.id.substring(0,10)+")"}function V(){f.value=!0,g.getWebauthnDevices({returnError:!0}).then(e=>{a.value=e.data}).catch(e=>{e.response.status===405?p.value=!0:n.error(e)}).finally(()=>{f.value=!1})}return K(e=>{e.name.startsWith("settings.")||n.clear()}),(e,u)=>{const m=h("FontAwesomeIcon"),B=h("FormCheckbox"),S=h("ButtonBackCloseCancel"),T=h("VueFooter"),N=h("FormWrapper");return d(),_("div",null,[o(G,{activeTab:"settings.webauthn.devices"},null,8,["activeTab"]),s("div",Q,[o(N,null,{default:v(()=>[t(p)?(d(),_("div",{key:0,class:"notification is-warning has-text-centered",innerHTML:e.$t("auth.auth_handled_by_proxy")},null,8,X)):y("",!0),s("h4",Y,i(e.$t("auth.webauthn.security_devices")),1),s("div",Z,i(e.$t("auth.webauthn.security_devices_legend")),1),s("div",ee,[s("a",{tabindex:"0",onClick:w,onKeyup:R(w,["enter"])},[o(m,{icon:["fas","plus-circle"]}),k(" "+i(e.$t("auth.webauthn.register_a_new_device")),1)],40,te)]),t(a).length>0?(d(),_("div",se,[(d(!0),_(j,null,E(t(a),c=>(d(),_("div",{key:c.id,class:"group-item is-size-5 is-size-6-mobile"},[k(i(F(c))+" ",1),o(t(H),null,{default:v(({mode:O})=>[s("button",{class:P(["button tag is-pulled-right",O==="dark"?"is-dark":"is-white"]),onClick:ue=>x(c.id),title:e.$t("settings.revoke")},i(e.$t("settings.revoke")),11,ne)]),_:2},1024)]))),128)),s("div",ae,i(e.$t("auth.webauthn.revoking_a_device_is_permanent")),1)])):y("",!0),o(q,{isVisible:t(f)&&t(a).length===0},null,8,["isVisible"]),s("h4",oe,i(e.$t("settings.options")),1),s("div",ie,i(e.$t("auth.webauthn.need_a_security_device_to_enable_options")),1),s("form",null,[o(B,{modelValue:t(l).preferences.useWebauthnOnly,"onUpdate:modelValue":u[0]||(u[0]=c=>t(l).preferences.useWebauthnOnly=c),fieldName:"useWebauthnOnly",label:"auth.webauthn.use_webauthn_only.label",help:"auth.webauthn.use_webauthn_only.help",disabled:t(p)||t(a).length===0},null,8,["modelValue","disabled"])]),o(T,{showButtons:!0},{default:v(()=>[o(S,{returnTo:{name:t($)},action:"close"},null,8,["returnTo"])]),_:1})]),_:1})])])}}};export{pe as default};
|
|
@ -0,0 +1 @@
|
|||
import{Q as z,a as U,b as L,u as M,a2 as A,r as b,o as D,x as I,J as v,a0 as K,e as h,f as d,g as _,m as o,h as s,p as g,i as t,l as y,t as i,w as R,j as k,F as j,G as E,U as H,n as J,I as r}from"./app-2d89b28f.js";import{_ as G}from"./SettingTabs-708dbaa6.js";import{w as P}from"./webauthnService-43ef310f.js";import{S as Q}from"./Spinner-aea2b665.js";/*! 2FAuth version 5.1.1 - Copyright (c) 2023 Bubka - https://github.com/Bubka/2FAuth */const q={class:"options-tabs"},X=["innerHTML"],Y={class:"title is-4 has-text-grey-light"},Z={class:"is-size-7-mobile"},ee={class:"mt-3"},te=["onKeyup"],se={key:1,class:"field"},ne=["onClick","title"],ae={class:"mt-2 is-size-7 is-pulled-right"},oe={class:"title is-4 pt-6 has-text-grey-light"},ie={class:"field"},_e={__name:"WebAuthn",setup(re){const C=z("2fauth"),l=U(),n=L(),W=M(),$=A(C.prefix+"returnTo","accounts"),a=b([]),f=b(!1),p=b(!1);D(()=>{V()}),I(()=>l.preferences.useWebauthnOnly,()=>{v.updatePreference("useWebauthnOnly",l.preferences.useWebauthnOnly).then(e=>{n.success({text:r("settings.forms.setting_saved")})})});function w(){if(p==!0)return n.warn({text:r("errors.unsupported_with_reverseproxy")}),!1;P.register().then(e=>{W.push({name:"settings.webauthn.editCredential",params:{credentialId:JSON.parse(e.config.data).id}})}).catch(e=>{var u;"webauthn"in e?e.name=="is-warning"?n.warn({text:r(e.message)}):n.alert({text:r(e.message)}):((u=e.response)==null?void 0:u.status)===422?n.alert({text:e.response.data.message}):n.error(e)})}function x(e){confirm(r("auth.confirm.revoke_device"))&&v.revokeWebauthnDevice(e).then(u=>{a.value=a.value.filter(m=>m.id!==e),a.value.length==0&&(l.preferences.useWebauthnOnly=!1),n.success({text:r("auth.webauthn.device_revoked")})})}function F(e){return e.alias?e.alias:r("auth.webauthn.my_device")+" (#"+e.id.substring(0,10)+")"}function V(){f.value=!0,v.getWebauthnDevices({returnError:!0}).then(e=>{a.value=e.data}).catch(e=>{e.response.status===405?p.value=!0:n.error(e)}).finally(()=>{f.value=!1})}return K(e=>{e.name.startsWith("settings.")||n.clear()}),(e,u)=>{const m=h("FontAwesomeIcon"),B=h("FormCheckbox"),S=h("ButtonBackCloseCancel"),T=h("VueFooter"),N=h("FormWrapper");return d(),_("div",null,[o(G,{activeTab:"settings.webauthn.devices"},null,8,["activeTab"]),s("div",q,[o(N,null,{default:g(()=>[t(p)?(d(),_("div",{key:0,class:"notification is-warning has-text-centered",innerHTML:e.$t("auth.auth_handled_by_proxy")},null,8,X)):y("",!0),s("h4",Y,i(e.$t("auth.webauthn.security_devices")),1),s("div",Z,i(e.$t("auth.webauthn.security_devices_legend")),1),s("div",ee,[s("a",{tabindex:"0",onClick:w,onKeyup:R(w,["enter"])},[o(m,{icon:["fas","plus-circle"]}),k(" "+i(e.$t("auth.webauthn.register_a_new_device")),1)],40,te)]),t(a).length>0?(d(),_("div",se,[(d(!0),_(j,null,E(t(a),c=>(d(),_("div",{key:c.id,class:"group-item is-size-5 is-size-6-mobile"},[k(i(F(c))+" ",1),o(t(H),null,{default:g(({mode:O})=>[s("button",{class:J(["button tag is-pulled-right",O==="dark"?"is-dark":"is-white"]),onClick:ue=>x(c.id),title:e.$t("settings.revoke")},i(e.$t("settings.revoke")),11,ne)]),_:2},1024)]))),128)),s("div",ae,i(e.$t("auth.webauthn.revoking_a_device_is_permanent")),1)])):y("",!0),o(Q,{isVisible:t(f)&&t(a).length===0},null,8,["isVisible"]),s("h4",oe,i(e.$t("auth.webauthn.options")),1),s("div",ie,i(e.$t("auth.webauthn.need_a_security_device_to_enable_options")),1),s("form",null,[o(B,{modelValue:t(l).preferences.useWebauthnOnly,"onUpdate:modelValue":u[0]||(u[0]=c=>t(l).preferences.useWebauthnOnly=c),fieldName:"useWebauthnOnly",label:"auth.webauthn.use_webauthn_only.label",help:"auth.webauthn.use_webauthn_only.help",disabled:t(p)||t(a).length===0},null,8,["modelValue","disabled"])]),o(T,{showButtons:!0},{default:g(()=>[o(S,{returnTo:{name:t($)},action:"close"},null,8,["returnTo"])]),_:1})]),_:1})])])}}};export{_e as default};
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue