CVE-2021-3819: Cross-Site Request Forgery (CSRF) (CWE-352)
value: firefly-iii is vulnerable to Cross-Site Request Forgery (CSRF)
CWE description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
CVSS v3 base score: 8.8
Published: 2021-09-27T13:15Z
Repository: https://github.com/firefly-iii/firefly-iii
Language: PHP
Files changed: app/Http/Controllers/CurrencyController.php, app/Http/Controllers/Transaction/CreateController.php, public/v1/js/ff/currencies/index.js, public/v1/js/ff/list/groups.js, public/v1/js/ff/transactions/show.js, resources/views/v1/currencies/index.twig, resources/views/v1/list/groups.twig, resources/views/v1/transactions/show.twig, routes/web.php
Commit date: 2021-09-20 06:39:10 +0200
Commit message: Convert GET routes to POST.

Diff: diff --git a/app/Http/Controllers/CurrencyController.php b/app/Http/Controllers/CurrencyController.php
index 22cd0a8cbad..c2781f39e33 100644
--- a/app/Http/Controllers/CurrencyController.php
+++ b/app/Http/Controllers/CurrencyController.php
@@ -211,56 +211,63 @@ public function destroy(Request $request, TransactionCurrency $currency)
      * @param TransactionCurrency $currency
      *
      * @return RedirectResponse|Redirector
      * @throws FireflyException
      */
-    public function disableCurrency(Request $request, TransactionCurrency $currency)
+    public function disableCurrency(Request $request)
     {
-        app('preferences')->mark();
+        $currencyId = (int)$request->get('id');
+        if ($currencyId > 0) {
+            // valid currency?
+            $currency = $this->repository->find($currencyId);
+            if (null !== $currency) {
+                app('preferences')->mark();
 
-        /** @var User $user */
-        $user = auth()->user();
-        if (!$this->userRepository->hasRole($user, 'owner')) {
+                /** @var User $user */
+                $user = auth()->user();
+                if (!$this->userRepository->hasRole($user, 'owner')) {
 
-            $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => e(config('firefly.site_owner'))]));
-            Log::channel('audit')->info(sprintf('Tried to disable currency %s but is not site owner.', $currency->code));
+                    $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => e(config('firefly.site_owner'))]));
+                    Log::channel('audit')->info(sprintf('Tried to disable currency %s but is not site owner.', $currency->code));
 
-            return redirect(route('currencies.index'));
+                    return redirect(route('currencies.index'));
 
-        }
+                }
 
-        if ($this->repository->currencyInUse($currency)) {
+                if ($this->repository->currencyInUse($currency)) {
 
-            $location = $this->repository->currencyInUseAt($currency);
-            $message  = (string)trans(sprintf('firefly.cannot_disable_currency_%s', $location), ['name' => e($currency->name)]);
+                    $location = $this->repository->currencyInUseAt($currency);
+                    $message  = (string)trans(sprintf('firefly.cannot_disable_currency_%s', $location), ['name' => e($currency->name)]);
 
-            $request->session()->flash('error', $message);
-            Log::channel('audit')->info(sprintf('Tried to disable currency %s but is in use.', $currency->code));
+                    $request->session()->flash('error', $message);
+                    Log::channel('audit')->info(sprintf('Tried to disable currency %s but is in use.', $currency->code));
 
-            return redirect(route('currencies.index'));
-        }
+                    return redirect(route('currencies.index'));
+                }
 
-        $this->repository->disable($currency);
-        Log::channel('audit')->info(sprintf('Disabled currency %s.', $currency->code));
-        // if no currencies are enabled, enable the first one in the DB (usually the EUR)
-        if (0 === $this->repository->get()->count()) {
-            /** @var TransactionCurrency $first */
-            $first = $this->repository->getAll()->first();
-            if (null === $first) {
-                throw new FireflyException('No currencies found.');
-            }
-            Log::channel('audit')->info(sprintf('Auto-enabled currency %s.', $first->code));
-            $this->repository->enable($first);
-            app('preferences')->set('currencyPreference', $first->code);
-            app('preferences')->mark();
-        }
+                $this->repository->disable($currency);
+                Log::channel('audit')->info(sprintf('Disabled currency %s.', $currency->code));
+                // if no currencies are enabled, enable the first one in the DB (usually the EUR)
+                if (0 === $this->repository->get()->count()) {
+                    /** @var TransactionCurrency $first */
+                    $first = $this->repository->getAll()->first();
+                    if (null === $first) {
+                        throw new FireflyException('No currencies found.');
+                    }
+                    Log::channel('audit')->info(sprintf('Auto-enabled currency %s.', $first->code));
+                    $this->repository->enable($first);
+                    app('preferences')->set('currencyPreference', $first->code);
+                    app('preferences')->mark();
+                }
 
-        if ('EUR' === $currency->code) {
-            session()->flash('warning', (string)trans('firefly.disable_EUR_side_effects'));
-        }
+                if ('EUR' === $currency->code) {
+                    session()->flash('warning', (string)trans('firefly.disable_EUR_side_effects'));
+                }
 
-        session()->flash('success', (string)trans('firefly.currency_is_now_disabled', ['name' => $currency->name]));
+                session()->flash('success', (string)trans('firefly.currency_is_now_disabled', ['name' => $currency->name]));
+            }
+        }
 
         return redirect(route('currencies.index'));
     }
 
     /**
@@ -309,17 +316,24 @@ public function edit(Request $request, TransactionCurrency $currency)
     /**
      * @param TransactionCurrency $currency
      *
      * @return RedirectResponse|Redirector
      */
-    public function enableCurrency(TransactionCurrency $currency)
+    public function enableCurrency(Request $request)
     {
-        app('preferences')->mark();
+        $currencyId = (int)$request->get('id');
+        if ($currencyId > 0) {
+            // valid currency?
+            $currency = $this->repository->find($currencyId);
+            if (null !== $currency) {
+                app('preferences')->mark();
 
-        $this->repository->enable($currency);
-        session()->flash('success', (string)trans('firefly.currency_is_now_enabled', ['name' => $currency->name]));
-        Log::channel('audit')->info(sprintf('Enabled currency %s.', $currency->code));
+                $this->repository->enable($currency);
+                session()->flash('success', (string)trans('firefly.currency_is_now_enabled', ['name' => $currency->name]));
+                Log::channel('audit')->info(sprintf('Enabled currency %s.', $currency->code));
+            }
+        }
 
         return redirect(route('currencies.index'));
     }
 
     /**
diff --git a/app/Http/Controllers/Transaction/CreateController.php b/app/Http/Controllers/Transaction/CreateController.php
index 4fc8d3c4264..5664a0e012f 100644
--- a/app/Http/Controllers/Transaction/CreateController.php
+++ b/app/Http/Controllers/Transaction/CreateController.php
@@ -26,63 +26,74 @@
 use FireflyIII\Events\StoredTransactionGroup;
 use FireflyIII\Exceptions\FireflyException;
 use FireflyIII\Http\Controllers\Controller;
 use FireflyIII\Models\TransactionGroup;
 use FireflyIII\Repositories\Account\AccountRepositoryInterface;
+use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
 use FireflyIII\Services\Internal\Update\GroupCloneService;
 use Illuminate\Contracts\View\Factory;
 use Illuminate\Contracts\View\View;
-use Illuminate\Http\RedirectResponse;
-use Illuminate\Routing\Redirector;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Request;
 
 /**
  * Class CreateController
  */
 class CreateController extends Controller
 {
+    private TransactionGroupRepositoryInterface $repository;
+
     /**
      * CreateController constructor.
      *
      * @codeCoverageIgnore
      */
     public function __construct()
     {
         parent::__construct();
 
         $this->middleware(
-            static function ($request, $next) {
+            function ($request, $next) {
                 app('view')->share('title', (string)trans('firefly.transactions'));
                 app('view')->share('mainTitleIcon', 'fa-exchange');
+                $this->repository = app(TransactionGroupRepositoryInterface::class);
 
                 return $next($request);
             }
         );
     }
 
     /**
-     * @param TransactionGroup $group
+     * @param Request $request
      *
-     * @return RedirectResponse|Redirector
+     * @return JsonResponse
      */
-    public function cloneGroup(TransactionGroup $group)
+    public function cloneGroup(Request $request): JsonResponse
     {
+        $groupId = (int)$request->get('id');
+        if (0 !== $groupId) {
+            $group = $this->repository->find($groupId);
+            if (null !== $group) {
+                /** @var GroupCloneService $service */
+                $service  = app(GroupCloneService::class);
+                $newGroup = $service->cloneGroup($group);
 
-        /** @var GroupCloneService $service */
-        $service  = app(GroupCloneService::class);
-        $newGroup = $service->cloneGroup($group);
+                // event!
+                event(new StoredTransactionGroup($newGroup));
 
-        // event!
-        event(new StoredTransactionGroup($newGroup));
+                app('preferences')->mark();
 
-        app('preferences')->mark();
+                $title = $newGroup->title ?? $newGroup->transactionJournals->first()->description;
+                $link  = route('transactions.show', [$newGroup->id]);
+                session()->flash('success', trans('firefly.stored_journal', ['description' => $title]));
+                session()->flash('success_url', $link);
 
-        $title = $newGroup->title ?? $newGroup->transactionJournals->first()->description;
-        $link  = route('transactions.show', [$newGroup->id]);
-        session()->flash('success', trans('firefly.stored_journal', ['description' => $title]));
-        session()->flash('success_url', $link);
+                return response()->json(['redirect' => route('transactions.show', [$newGroup->id])]);
+            }
+        }
 
-        return redirect(route('transactions.show', [$newGroup->id]));
+        return response()->json(['redirect' => route('transactions.show', [$groupId])]);
     }
 
     /**
      * Create a new transaction group.
      *
diff --git a/public/v1/js/ff/currencies/index.js b/public/v1/js/ff/currencies/index.js
index eb0ad1f1f2e..dbccfae942f 100644
--- a/public/v1/js/ff/currencies/index.js
+++ b/public/v1/js/ff/currencies/index.js
@@ -23,10 +23,12 @@
  */
 $(function () {
     "use strict";
     $('.make_default').on('click', setDefaultCurrency);
 
+    $('.enable-currency').on('click', enableCurrency);
+    $('.disable-currency').on('click', disableCurrency);
 });
 
 function setDefaultCurrency(e) {
     var button = $(e.currentTarget);
     var currencyId = parseInt(button.data('id'));
@@ -38,6 +40,39 @@ function setDefaultCurrency(e) {
         // lame but it works
         location.reload();
     }).fail(function () {
         console.error('I failed :(');
     });
+    return false;
+}
+
+function enableCurrency(e) {
+    var button = $(e.currentTarget);
+    var currencyId = parseInt(button.data('id'));
+
+    $.post(enableCurrencyUrl, {
+        _token: token,
+        id: currencyId
+    }).done(function (data) {
+        // lame but it works
+        location.reload();
+    }).fail(function () {
+        console.error('I failed :(');
+    });
+    return false;
+}
+
+function disableCurrency(e) {
+    var button = $(e.currentTarget);
+    var currencyId = parseInt(button.data('id'));
+
+    $.post(disableCurrencyUrl, {
+        _token: token,
+        id: currencyId
+    }).done(function (data) {
+        // lame but it works
+        location.reload();
+    }).fail(function () {
+        console.error('I failed :(');
+    });
+    return false;
 }
diff --git a/public/v1/js/ff/list/groups.js b/public/v1/js/ff/list/groups.js
index 98bc371cf56..a18a1cca6e2 100644
--- a/public/v1/js/ff/list/groups.js
+++ b/public/v1/js/ff/list/groups.js
@@ -21,10 +21,11 @@
 var count = 0;
 
 $(document).ready(function () {
     updateListButtons();
     addSort();
+    $('.clone-transaction').click(cloneTransaction);
 });
 
 var fixHelper = function (e, tr) {
     "use strict";
     var $originals = tr.children();
@@ -204,6 +205,21 @@ function updateActionButtons() {
 
     }
     if (0 === count) {
         $('.action-menu').hide();
     }
-}
\ No newline at end of file
+}
+function cloneTransaction(e) {
+    var button = $(e.currentTarget);
+    var groupId = parseInt(button.data('id'));
+
+    $.post(cloneGroupUrl, {
+        _token: token,
+        id: groupId
+    }).done(function (data) {
+        // lame but it works
+        location.href = data.redirect;
+    }).fail(function () {
+        console.error('I failed :(');
+    });
+    return false;
+}
diff --git a/public/v1/js/ff/transactions/show.js b/public/v1/js/ff/transactions/show.js
index ada41fc76be..72ad1490b3a 100644
--- a/public/v1/js/ff/transactions/show.js
+++ b/public/v1/js/ff/transactions/show.js
@@ -21,10 +21,11 @@
 /** global: autoCompleteUri */
 
 $(function () {
     "use strict";
     $('.link-modal').click(getLinkModal);
+    $('.clone-transaction').click(cloneTransaction);
     $('#linkJournalModal').on('shown.bs.modal', function () {
         makeAutoComplete();
     })
     $('[data-toggle="tooltip"]').tooltip();
 });
@@ -78,5 +79,21 @@ function selectedJournal(event, journal) {
     $('#journal-selector').hide();
     $('#journal-selection').show();
     $('#selected-journal').html('<a href="' + groupURI.replace('%GROUP%', journal.transaction_group_id) + '">' + journal.description + '</a>').show();
     $('input[name="opposing"]').val(journal.id);
 }
+
+function cloneTransaction(e) {
+    var button = $(e.currentTarget);
+    var groupId = parseInt(button.data('id'));
+
+    $.post(cloneGroupUrl, {
+        _token: token,
+        id: groupId
+    }).done(function (data) {
+        // lame but it works
+        location.href = data.redirect;
+    }).fail(function () {
+        console.error('I failed :(');
+    });
+    return false;
+}
\ No newline at end of file
diff --git a/resources/views/v1/currencies/index.twig b/resources/views/v1/currencies/index.twig
index 8320003ba76..40ecba91111 100644
--- a/resources/views/v1/currencies/index.twig
+++ b/resources/views/v1/currencies/index.twig
@@ -67,18 +67,18 @@
                                             {% if currency.id != defaultCurrency.id %}
                                                 <button data-id="{{ currency.id }}" class="make_default btn btn-default"><span
                                                             class="fa fa-fw fa-star"></span> {{ 'make_default_currency'|_ }}</button>
                                             {% endif %}
                                             {% if currency.enabled %}
-                                                <a class="btn btn-default"
-                                                   href="{{ route('currencies.disable',currency.id) }}">
+                                                <a class="btn btn-default disable-currency" data-id="{{ currency.id }}"
+                                                   href="#">
                                                     <span class="fa fa-fw fa-square-o"></span>
                                                     {{ 'disable_currency'|_ }}</a>
                                             {% endif %}
                                             {% if not currency.enabled %}
-                                                <a class="btn btn-default"
-                                                   href="{{ route('currencies.enable',currency.id) }}">
+                                                <a class="btn btn-default enable-currency" data-id="{{ currency.id }}"
+                                                   href="#">
                                                     <span class="fa fa-fw fa-check-square-o"></span>
                                                     {{ 'enable_currency'|_ }}</a>
                                             {% endif %}
                                         </div>
                                     </td>
@@ -99,8 +99,10 @@
     </div>
 {% endblock %}
 {% block scripts %}
     <script type="text/javascript" nonce="{{ JS_NONCE }}">
         var makeDefaultUrl = "{{ route('currencies.default') }}";
+        var disableCurrencyUrl = "{{ route('currencies.disable') }}";
+        var enableCurrencyUrl = "{{ route('currencies.enable') }}";
     </script>
     <script type="text/javascript" src="v1/js/ff/currencies/index.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
 {% endblock %}
diff --git a/resources/views/v1/list/groups.twig b/resources/views/v1/list/groups.twig
index e799367791a..1ce20727fef 100644
--- a/resources/views/v1/list/groups.twig
+++ b/resources/views/v1/list/groups.twig
@@ -83,11 +83,11 @@
                         <ul class="dropdown-menu dropdown-menu-right" role="menu">
                             <li><a href="{{ route('transactions.edit', [group.id]) }}"><span
                                             class="fa fa-fw fa-pencil"></span> {{ 'edit'|_ }}</a></li>
                             <li><a href="{{ route('transactions.delete', [group.id]) }}"><span
                                             class="fa fa-fw fa-trash"></span> {{ 'delete'|_ }}</a></li>
-                            <li><a href="{{ route('transactions.clone', [group.id]) }}"><span
+                            <li><a href="#" data-id="{{ group.id }}" class="clone-transaction"><span
                                             class="fa fa-copy fa-fw"></span> {{ 'clone'|_ }}</a></li>
                         </ul>
                     </div>
                 </td>
                 <td style="border-top:1px #aaa solid;" class="hidden-xs">&nbsp;</td>
@@ -247,11 +247,11 @@
                             <ul class="dropdown-menu dropdown-menu-right" role="menu">
                                 <li><a href="{{ route('transactions.edit', [group.id]) }}"><span
                                                 class="fa fa-fw fa-pencil"></span> {{ 'edit'|_ }}</a></li>
                                 <li><a href="{{ route('transactions.delete', [group.id]) }}"><span
                                                 class="fa fa-fw fa-trash"></span> {{ 'delete'|_ }}</a></li>
-                                <li><a href="{{ route('transactions.clone', [group.id]) }}"><span
+                                <li><a href="#" data-id="{{ group.id }}" class="clone-transaction"><span
                                                 class="fa fa-copy fa-fw"></span> {{ 'clone'|_ }}</a></li>
                                 <li>
                                     <a href="{{ route('rules.create-from-journal', [transaction.transaction_journal_id]) }}"><span
                                                 class="fa fa-fw fa-random"></span> {{ 'create_rule_from_transaction'|_ }}
                                     </a></li>
@@ -307,5 +307,8 @@
             <td colspan="8" class="no-margin-pagination">{{ groups.links('v1.pagination.bootstrap-4')|raw }}</td>
         {% endif %}
     </tr>
     </tfoot>
 </table>
+<script type="text/javascript" nonce="{{ JS_NONCE }}">
+    var cloneGroupUrl = '{{ route('transactions.clone') }}';
+</script>
diff --git a/resources/views/v1/transactions/show.twig b/resources/views/v1/transactions/show.twig
index 668c9c3609a..f00d0303bd7 100644
--- a/resources/views/v1/transactions/show.twig
+++ b/resources/views/v1/transactions/show.twig
@@ -33,13 +33,12 @@
                                 <li><a href="{{ route('transactions.convert.index', ['transfer', transactionGroup.id]) }}"><span class="fa fa-exchange"></span> {{ 'convert_to_transfer'|_ }}</a></li>
                             {% endif %}
 
                             {#  clone #}
                             {% if groupArray.transactions[0].type != 'opening balance' and groupArray.transactions[0].type != 'reconciliation' %}
-                                <!-- since 5.1.0 -->
                                 <li role="separator" class="divider"></li>
-                                <li><a href="{{ route('transactions.clone', [transactionGroup.id]) }}"><span class="fa fa-copy"></span> {{ 'clone'|_ }}</a></li>
+                                <li><a href="#" class="clone-transaction" data-id="{{ transactionGroup.id }}"><span class="fa fa-copy"></span> {{ 'clone'|_ }}</a></li>
                             {% endif %}
 
                         </ul>
                     </div>
                 </div>
@@ -206,22 +205,20 @@
                                     <li><a href="{{ route('transactions.convert.index', ['transfer', transactionGroup.id]) }}"><span class="fa fa-exchange"></span> {{ 'convert_to_transfer'|_ }}</a></li>
                                 {% endif %}
 
                                 {#  clone #}
                                 {% if groupArray.transactions[0].type != 'opening balance' and groupArray.transactions[0].type != 'reconciliation' %}
-                                    <!-- since 5.1.0 -->
                                     <li role="separator" class="divider"></li>
-                                    <li><a href="{{ route('transactions.clone', [transactionGroup.id]) }}"><span class="fa fa-copy"></span> {{ 'clone'|_ }}</a></li>
+                                    <li><a href="#" data-id="{{ transactionGroup.id }}" class="clone-transaction"><span class="fa fa-copy"></span> {{ 'clone'|_ }}</a></li>
                                 {% endif %}
 
                                 <li><a href="#" class="link-modal" data-journal="{{ journal.transaction_journal_id }}"><span class="fa fa-fw fa-link"></span>{{ 'link_transaction'|_ }}</a></li>
                                 <li role="separator" class="divider"></li>
                                 <li><a href="{{ route('rules.create-from-journal', [journal.transaction_journal_id]) }}"><span class="fa fa-fw fa-random"></span>{{ 'create_rule_from_transaction'|_ }}</a></li>
                                 <li><a href="{{ route('recurring.create-from-journal', [journal.transaction_journal_id]) }}"><span class="fa fa-fw fa-paint-brush"></span>{{ 'create_recurring_from_transaction'|_ }}</a></li>
                             </ul>
                         </div>
-
                     </div>
                     <div class="box-body no-padding">
                         <table class="table">
                             <tr>
                                 <td colspan="2">
@@ -423,10 +420,11 @@
 {% block scripts %}
     <script type="text/javascript" nonce="{{ JS_NONCE }}">
         var modalDialogURI = '{{ route('transactions.link.modal', ['%JOURNAL%']) }}';
         var acURI = '{{ route('api.v1.autocomplete.transactions-with-id') }}';
         var groupURI = '{{ route('transactions.show',['%GROUP%']) }}';
+        var cloneGroupUrl = '{{ route('transactions.clone') }}';
     </script>
     <script type="text/javascript" src="v1/js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
     <script type="text/javascript" src="v1/js/ff/transactions/show.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
 {% endblock %}
 
diff --git a/routes/web.php b/routes/web.php
index d9b42c4b446..5452cae5dc7 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -336,12 +336,12 @@ static function () {
         Route::get('', ['uses' => 'CurrencyController@index', 'as' => 'index']);
         Route::get('create', ['uses' => 'CurrencyController@create', 'as' => 'create']);
         Route::get('edit/{currency}', ['uses' => 'CurrencyController@edit', 'as' => 'edit']);
         Route::get('delete/{currency}', ['uses' => 'CurrencyController@delete', 'as' => 'delete']);
         Route::post('default', ['uses' => 'CurrencyController@defaultCurrency', 'as' => 'default']);
-        Route::get('enable/{currency}', ['uses' => 'CurrencyController@enableCurrency', 'as' => 'enable']);
-        Route::get('disable/{currency}', ['uses' => 'CurrencyController@disableCurrency', 'as' => 'disable']);
+        Route::post('enable', ['uses' => 'CurrencyController@enableCurrency', 'as' => 'enable']);
+        Route::post('disable', ['uses' => 'CurrencyController@disableCurrency', 'as' => 'disable']);
 
         Route::post('store', ['uses' => 'CurrencyController@store', 'as' => 'store']);
         Route::post('update/{currency}', ['uses' => 'CurrencyController@update', 'as' => 'update']);
         Route::post('destroy/{currency}', ['uses' => 'CurrencyController@destroy', 'as' => 'destroy']);
     }
@@ -1010,11 +1010,11 @@ static function () {
         // create group:
         Route::get('create/{objectType}', ['uses' => 'Transaction\CreateController@create', 'as' => 'create']);
         Route::post('store', ['uses' => 'Transaction\CreateController@store', 'as' => 'store']);
 
         // clone group
-        Route::get('clone/{transactionGroup}', ['uses' => 'Transaction\CreateController@cloneGroup', 'as' => 'clone']);
+        Route::post('clone', ['uses' => 'Transaction\CreateController@cloneGroup', 'as' => 'clone']);
 
         // edit group
         Route::get('edit/{transactionGroup}', ['uses' => 'Transaction\EditController@edit', 'as' => 'edit']);
         Route::post('update', ['uses' => 'Transaction\EditController@update', 'as' => 'update']);
 

Vulnerable code:     public function disableCurrency(Request $request, TransactionCurrency $currency)
        app('preferences')->mark();
        /** @var User $user */
        $user = auth()->user();
        if (!$this->userRepository->hasRole($user, 'owner')) {
            $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => e(config('firefly.site_owner'))]));
            Log::channel('audit')->info(sprintf('Tried to disable currency %s but is not site owner.', $currency->code));
            return redirect(route('currencies.index'));
        }
        if ($this->repository->currencyInUse($currency)) {
            $location = $this->repository->currencyInUseAt($currency);
            $message  = (string)trans(sprintf('firefly.cannot_disable_currency_%s', $location), ['name' => e($currency->name)]);
            $request->session()->flash('error', $message);
            Log::channel('audit')->info(sprintf('Tried to disable currency %s but is in use.', $currency->code));
            return redirect(route('currencies.index'));
        }
        $this->repository->disable($currency);
        Log::channel('audit')->info(sprintf('Disabled currency %s.', $currency->code));
        // if no currencies are enabled, enable the first one in the DB (usually the EUR)
        if (0 === $this->repository->get()->count()) {
            /** @var TransactionCurrency $first */
            $first = $this->repository->getAll()->first();
            if (null === $first) {
                throw new FireflyException('No currencies found.');
            }
            Log::channel('audit')->info(sprintf('Auto-enabled currency %s.', $first->code));
            $this->repository->enable($first);
            app('preferences')->set('currencyPreference', $first->code);
            app('preferences')->mark();
        }
        if ('EUR' === $currency->code) {
            session()->flash('warning', (string)trans('firefly.disable_EUR_side_effects'));
        }
        session()->flash('success', (string)trans('firefly.currency_is_now_disabled', ['name' => $currency->name]));
    public function enableCurrency(TransactionCurrency $currency)
        app('preferences')->mark();
        $this->repository->enable($currency);
        session()->flash('success', (string)trans('firefly.currency_is_now_enabled', ['name' => $currency->name]));
        Log::channel('audit')->info(sprintf('Enabled currency %s.', $currency->code));
use Illuminate\Http\RedirectResponse;
use Illuminate\Routing\Redirector;
            static function ($request, $next) {
     * @param TransactionGroup $group
     * @return RedirectResponse|Redirector
    public function cloneGroup(TransactionGroup $group)
        /** @var GroupCloneService $service */
        $service  = app(GroupCloneService::class);
        $newGroup = $service->cloneGroup($group);
        // event!
        event(new StoredTransactionGroup($newGroup));
        app('preferences')->mark();
        $title = $newGroup->title ?? $newGroup->transactionJournals->first()->description;
        $link  = route('transactions.show', [$newGroup->id]);
        session()->flash('success', trans('firefly.stored_journal', ['description' => $title]));
        session()->flash('success_url', $link);
        return redirect(route('transactions.show', [$newGroup->id]));
}
                                                <a class="btn btn-default"
                                                   href="{{ route('currencies.disable',currency.id) }}">
                                                <a class="btn btn-default"
                                                   href="{{ route('currencies.enable',currency.id) }}">
                            <li><a href="{{ route('transactions.clone', [group.id]) }}"><span
                                <li><a href="{{ route('transactions.clone', [group.id]) }}"><span
                                <!-- since 5.1.0 -->
                                <li><a href="{{ route('transactions.clone', [transactionGroup.id]) }}"><span class="fa fa-copy"></span> {{ 'clone'|_ }}</a></li>
                                    <!-- since 5.1.0 -->
                                    <li><a href="{{ route('transactions.clone', [transactionGroup.id]) }}"><span class="fa fa-copy"></span> {{ 'clone'|_ }}</a></li>

        Route::get('enable/{currency}', ['uses' => 'CurrencyController@enableCurrency', 'as' => 'enable']);
        Route::get('disable/{currency}', ['uses' => 'CurrencyController@disableCurrency', 'as' => 'disable']);
        Route::get('clone/{transactionGroup}', ['uses' => 'Transaction\CreateController@cloneGroup', 'as' => 'clone']);
Fixed code:     public function disableCurrency(Request $request)
        $currencyId = (int)$request->get('id');
        if ($currencyId > 0) {
            // valid currency?
            $currency = $this->repository->find($currencyId);
            if (null !== $currency) {
                app('preferences')->mark();
                /** @var User $user */
                $user = auth()->user();
                if (!$this->userRepository->hasRole($user, 'owner')) {
                    $request->session()->flash('error', (string)trans('firefly.ask_site_owner', ['owner' => e(config('firefly.site_owner'))]));
                    Log::channel('audit')->info(sprintf('Tried to disable currency %s but is not site owner.', $currency->code));
                    return redirect(route('currencies.index'));
                }
                if ($this->repository->currencyInUse($currency)) {
                    $location = $this->repository->currencyInUseAt($currency);
                    $message  = (string)trans(sprintf('firefly.cannot_disable_currency_%s', $location), ['name' => e($currency->name)]);
                    $request->session()->flash('error', $message);
                    Log::channel('audit')->info(sprintf('Tried to disable currency %s but is in use.', $currency->code));
                    return redirect(route('currencies.index'));
                }
                $this->repository->disable($currency);
                Log::channel('audit')->info(sprintf('Disabled currency %s.', $currency->code));
                // if no currencies are enabled, enable the first one in the DB (usually the EUR)
                if (0 === $this->repository->get()->count()) {
                    /** @var TransactionCurrency $first */
                    $first = $this->repository->getAll()->first();
                    if (null === $first) {
                        throw new FireflyException('No currencies found.');
                    }
                    Log::channel('audit')->info(sprintf('Auto-enabled currency %s.', $first->code));
                    $this->repository->enable($first);
                    app('preferences')->set('currencyPreference', $first->code);
                    app('preferences')->mark();
                }
                if ('EUR' === $currency->code) {
                    session()->flash('warning', (string)trans('firefly.disable_EUR_side_effects'));
                }
                session()->flash('success', (string)trans('firefly.currency_is_now_disabled', ['name' => $currency->name]));
            }
        }
    public function enableCurrency(Request $request)
        $currencyId = (int)$request->get('id');
        if ($currencyId > 0) {
            // valid currency?
            $currency = $this->repository->find($currencyId);
            if (null !== $currency) {
                app('preferences')->mark();
                $this->repository->enable($currency);
                session()->flash('success', (string)trans('firefly.currency_is_now_enabled', ['name' => $currency->name]));
                Log::channel('audit')->info(sprintf('Enabled currency %s.', $currency->code));
            }
        }
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
    private TransactionGroupRepositoryInterface $repository;

            function ($request, $next) {
                $this->repository = app(TransactionGroupRepositoryInterface::class);
     * @param Request $request
     * @return JsonResponse
    public function cloneGroup(Request $request): JsonResponse
        $groupId = (int)$request->get('id');
        if (0 !== $groupId) {
            $group = $this->repository->find($groupId);
            if (null !== $group) {
                /** @var GroupCloneService $service */
                $service  = app(GroupCloneService::class);
                $newGroup = $service->cloneGroup($group);
                // event!
                event(new StoredTransactionGroup($newGroup));
                app('preferences')->mark();
                $title = $newGroup->title ?? $newGroup->transactionJournals->first()->description;
                $link  = route('transactions.show', [$newGroup->id]);
                session()->flash('success', trans('firefly.stored_journal', ['description' => $title]));
                session()->flash('success_url', $link);
                return response()->json(['redirect' => route('transactions.show', [$newGroup->id])]);
            }
        }
        return response()->json(['redirect' => route('transactions.show', [$groupId])]);
    $('.enable-currency').on('click', enableCurrency);
    $('.disable-currency').on('click', disableCurrency);
    return false;
}

function enableCurrency(e) {
    var button = $(e.currentTarget);
    var currencyId = parseInt(button.data('id'));

    $.post(enableCurrencyUrl, {
        _token: token,
        id: currencyId
    }).done(function (data) {
        // lame but it works
        location.reload();
    }).fail(function () {
        console.error('I failed :(');
    });
    return false;
}

function disableCurrency(e) {
    var button = $(e.currentTarget);
    var currencyId = parseInt(button.data('id'));

    $.post(disableCurrencyUrl, {
        _token: token,
        id: currencyId
    }).done(function (data) {
        // lame but it works
        location.reload();
    }).fail(function () {
        console.error('I failed :(');
    });
    return false;
    $('.clone-transaction').click(cloneTransaction);
}
function cloneTransaction(e) {
    var button = $(e.currentTarget);
    var groupId = parseInt(button.data('id'));

    $.post(cloneGroupUrl, {
        _token: token,
        id: groupId
    }).done(function (data) {
        // lame but it works
        location.href = data.redirect;
    }).fail(function () {
        console.error('I failed :(');
    });
    return false;
}
    $('.clone-transaction').click(cloneTransaction);

function cloneTransaction(e) {
    var button = $(e.currentTarget);
    var groupId = parseInt(button.data('id'));

    $.post(cloneGroupUrl, {
        _token: token,
        id: groupId
    }).done(function (data) {
        // lame but it works
        location.href = data.redirect;
    }).fail(function () {
        console.error('I failed :(');
    });
    return false;
}
                                                <a class="btn btn-default disable-currency" data-id="{{ currency.id }}"
                                                   href="#">
                                                <a class="btn btn-default enable-currency" data-id="{{ currency.id }}"
                                                   href="#">
        var disableCurrencyUrl = "{{ route('currencies.disable') }}";
        var enableCurrencyUrl = "{{ route('currencies.enable') }}";
                            <li><a href="#" data-id="{{ group.id }}" class="clone-transaction"><span
                                <li><a href="#" data-id="{{ group.id }}" class="clone-transaction"><span
<script type="text/javascript" nonce="{{ JS_NONCE }}">
    var cloneGroupUrl = '{{ route('transactions.clone') }}';
</script>
                                <li><a href="#" class="clone-transaction" data-id="{{ transactionGroup.id }}"><span class="fa fa-copy"></span> {{ 'clone'|_ }}</a></li>
                                    <li><a href="#" data-id="{{ transactionGroup.id }}" class="clone-transaction"><span class="fa fa-copy"></span> {{ 'clone'|_ }}</a></li>
        var cloneGroupUrl = '{{ route('transactions.clone') }}';
        Route::post('enable', ['uses' => 'CurrencyController@enableCurrency', 'as' => 'enable']);
        Route::post('disable', ['uses' => 'CurrencyController@disableCurrency', 'as' => 'disable']);
        Route::post('clone', ['uses' => 'Transaction\CreateController@cloneGroup', 'as' => 'clone']);