Backend access rights for news records - typo3

Is it possible to store all records of the news extension (ext:news) on the same storage page, but show only records, which are created by the loggedin backend user?
So the current backend user can just see and edit his own records? Admins should see all records of course.

No, this is not possible, since backend user permissions on record level are not implemented in TYPO3.
So you either have to separate the news records of the users in separate sysfolders or you could try to use hooks (e.g. $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.db_list_extra.inc']['getTable']) or XClass to customize TYPO3 backend to your needs. I do not recommend the latter, since the TYPO3 backend permission system is complex and you would need to make sure to restrict record access in several parts of TYPO3 (e.g. recordlist, element browser, related news field, ...)

There are two ways to archive that:
If the backend user is not too much. You can just create a page(type
is folder) named with the backend user name. And in the backend user
module you can set the permission(Not for the group user but for the
single backend user only).
if the backend user is too much. and You just wanna set permissions for the group and all backend users are sharing the same rules. You can refer to Hook:https://docs.typo3.org/p/georgringer/news/main/en-us/Tutorials/ExtendNews/Hooks/Index.html so that the basic logic is like this:
2.1 get current logged-in user group.
2.2 if the group is Reporter, we can use the hook for the listing page:
$constraints[] = $query->equals('cruser_id', $be_id);
Edit(3/3/2022):
Hi Chris, Yes you are right.
Today, I have got a chance to dig into the news extension. I think we can still make it by the hook
in your ext_localconf.php
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][\TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList::class]['modifyQuery'][$_EXTKEY]
= \T3docs\SitePackage\Hooks\DatabaseRecordListHook::class;
(Please make sure the namespace is correct)
within the file : T3docs\SitePackage\Hooks\DatabaseRecordListHook.Create a function named modifyQuery:
public function modifyQuery($parameters,
$table,
$pageId,
$additionalConstraints,
$fields,
$queryBuilder)
{
if ($table === 'tx_news_domain_model_news') {
$tsconfig = $GLOBALS['BE_USER']->getTSConfig();
if (!empty($tsconfig['options.']['be_users'])) {
$be_users = $tsconfig['options.']['be_users'];
$queryBuilder->andWhere('cruser_id IN (' . $be_users . ')');
}
}
return $queryBuilder;
}
in the user Options tab set Tsconfg : options.be_users = 3,100
(3,100 is the be_user id. And the 2 two backend users's news will show up)
thus, it works for me.

Related

TYPO3 v10: How to access TSFE in Backend/Scheduler Task?

The current situation:
I am trying to access the TypoScript configuration of the frontend from within the backend (or rather a scheduler task). Previously with Typo3 v8 and v9, I initialized entire $GLOBALS["TSFE"] object, however this was already hack the last time around (using mostly deprecated calls) and now it has all been removed with the v10 release.
My goal:
Access the TypoScript configuration of the frontend of a certain page (root page of a site would be fine) from within a scheduler job.
Background of the whole project:
I have a periodic scheduler job that sends emails to various users (fe_users). The email contains links to certain pages (configured UIDs in typoscript) as well as file attachments and the likes (generated by other extensions, which are also fully configured via typoscript). Currently, I basically initialize the entire frontend from within the backend, but as I said before, its inefficient, super hacky and I doubt it was the intended way to solve this problem.
Getting TypoScript settings in the backend is ugly, but possible.
You need a page ID and a rootline which you can pass to \TYPO3\CMS\Core\TypoScript\TemplateService::runThroughTemplates().
Something along these lines:
$template = GeneralUtility::makeInstance(TemplateService::class);
$template->tt_track = false;
$rootline = GeneralUtility::makeInstance(
RootlineUtility::class, $pageId
)->get();
$template->runThroughTemplates($rootline, 0);
$template->generateConfig();
$typoScriptSetup = $template->setup;
You can get inspiration from \TYPO3\CMS\Extbase\Configuration\BackendConfigurationManager::getTypoScriptSetup and \TYPO3\CMS\Tstemplate\Controller\TypoScriptTemplateObjectBrowserModuleFunctionController
This won't get any better and is not intended to be done such way. I would use as configuration:
plain PHP, e.g. in $GLOBALS['TYPO3_CONF_VARS]`
YAML site config if depending on various sites
You can build links by using e.g. something like that
protected function generateUrl(int $pageId, int $recordId)
{
$additionalParams = 'tx_xxxx[action]=show&tx_ixxxx[controller]=Job&tx_xxxx[job]=' . $recordId;
return BackendUtility::getPreviewUrl($pageId, '', null, '', '', $additionalParams);
}

Access form data in multiple actions on same page

I'm trying to access a submitted object from one action in an other action on the same page, while making it available to both actions.
Example: Site /search/ has two plugins embedded:
SearchPlugin
shows search form
submits form to itself
ResultPlugin
should get form data from SearchPlugin
Now if I submit the SearchPlugin form data to itself I only have the form data available in the SearchPlugin action, not in the ResultPlugin. If it submit the SearchPlugin form to the ResultPlugin action I only have the data available in the ResultPlugin, not in the SearchPlugin.
I need the data to be available in both plugins/actions on the same site after submitting.
Is this somehow possible?
Built-in Extbase configuration option
You can use view.pluginNamespace to have both plugins use the same HTTP parameter namespace, (e.g. search instead of tx_extension_plugin1/tx_extension_plugin2): https://docs.typo3.org/m/typo3/book-extbasefluid/master/en-us/b-ExtbaseReference/Index.html
That will make your Extbase actions be able to "share" all parameters. Make sure that all your plugin actions are ready for that.
(Keep in mind though that this "feature" might be deprecated once probably because it adds some architectural burden to efficient resolving of routing configurations. But that is just rumor and I got the idea from here: https://github.com/TYPO3-Documentation/TYPO3CMS-Book-ExtbaseFluid/pull/379/files)
Custom solution
You can manually set up parameters for your Extbase actions in an initializeAction:
$pluginContexts = [
'tx_extension_plugin2',
];
// look for a SearchObject in a different (HTTP) plugin namespace
if (!$this->request->hasArgument('searchObject')) {
foreach ($pluginContexts as $pluginContext) {
$foreignPluginContext = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP($pluginContext);
if (isset($foreignPluginContext['searchObject'])) {
$searchObject = $foreignPluginContext['searchObject'];
// if needed do some mapping to object here or validate
...
$this->request->setArgument('searchObject', $searchObject);
break;
}
}
}

How to implement dynamic creation of permission groups with different set of endpoints Django Rest Framework

In my project I have lot of endpoint views (APIViews, ViewSets). For all of them now I set permissions, some of them are default (e.g. AllowAny) and some are custom created:
permission_classes = (IsUserHaveSomePermission,)
Now I want to implement some flexible system, that will allow me to specify set of allowed endpoints for each user, for example:
On front-end I want to select some user and have a list of checkboxes that correspond to project's endpoints.
This is just an utopian solution, some details may be changed, but the main question is to how make something similar so that admins can basically dynamically change list of allowed endpoints/views for user?
thanks in advance
This solution can be implemented by storing if the user has permission to access the current request method and request path.
Create a new db model for storing the user, request method and request path. Lets say the name of the model is RequestPermission
Instead of the path you can store a constant representing the url so that you have the flexibility of editing the path later on. This constant can be the url name which is supported by django.
class RequestPermission(models.Model):
user = user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='request_permissions')
method = models.CharField(max_length=10)
path_name = models.CharField(max_length=200)
create a custom permission class:
class IsUserResuestAllowed(permissions.BasePermission):
def has_permission(self, request, view):
user = request.user
# you can choose how to get the path_name from the path
path_name = get_path_name(request.path)
return RequestPermission.objects.filter(user=user, method=request.method, path_name=path_name).exists()
Now you can use this class as the default permission class in rest framework settings or use it per view.

How to Disallow User Access to CRUD Backend

I've got Backpack for Laravel installed and have been using it for some time as an admin back end on a project. I'm also using the spatie/permission module (might come with Backpack, can't remember) to create users for the front end.
Currently, all users are able to access both front end and back end regardless of the group they belong to. I'd like to change that so that only members in an "admin" group are able to access the back end. I've followed the instructions here for separating front end and back end sessions but that's not really what I want as all users are still able to access both sites of the project.
I'm guessing I need to add a guard to the CRUD routes but I'm finding it to be much harder than it should be. Any pointers on how to do this would be greatly appreciated. TIA.
You can create a new middleware and use it in your routes group for admin routes.
To create a new middleware use the php artisan command like so: (you can name the new middleware what ever you want:
php artisan make:middleware RequireAdminRole
Now, inside your new middleware, on the handle function you can have something like this that returns a 403 Forbidden error message:
public function handle($request, Closure $next)
{
$user = auth()->user();
if (!$user) return $next($request);
if (!$user->hasRole('Admin'))
{
// if your sessions are decoupled from the frontend
// you can even logout the user like so:
// auth()->logout();
abort(403, 'Access denied');
}
return $next($request);
}
Here we are using the hasRole method, but there are more that you can use. See the spatie/laravel-permissions documentation for more info.
Now, let's assign this middleware a 'name' so we can use it in our route groups for the admin. Inside the App\Kernel.php file, in the end, inside the $routeMiddleware array add it and give it a new, for example:
'isadmin' => \App\Http\Middleware\RequireAdminRole::class,
And finally, you can add this middleware to your admin routes group (which should be in custom.php file if you're using latest backpack 3.4 version) :
Route::group([
'prefix' => 'admin',
'middleware' => ['web', 'isadmin', config('backpack.base.middleware_key', 'admin')],
'namespace' => 'App\Http\Controllers\Admin',
], function () {
// your routes are here
});
Now all your requests to the admin routes should be protected by the user role check.
Please let us know how it went for you, and if you encountered any issues.
Best regards,
~Cristian

Symfony2 Form Rest Api only add to Relation

I am using Symfony2 as Rest Api for a JS Frontend App. I came across a scenario where I want users to "invite" (=add) Users to a Group. But I want to only allow them to add Users to the existing Relation and not "overwrite" the whole relation, which is the standard behaviour in combination with a regular Symfony2 Form.
What would be the best practice to achieve this behaviour?
Additional Comment:
I am using Ember-Data in the frontend and my frontend would probably send a put request with the whole Group including additional users (but not all).
My JSON Payload would look something like this:
{
"usergroup": {
"name":"yxcv2",
"stake":"sdfghj",
"imageName":null,
"userCount":5,
"users":[
5,
6,
7
],
"gameGroup":"13",
}
}
In this scenario User 1,2,3 and 4 are already members of the group. And instead of replacing 1,2,3,4 with 5,6,7, I want to ADD 5,6,7 to the already existing members.
A LINK request should be used to add an item to an existing collection instead of overwriting it with a POST request.
Using a symfony form you'd post the User (id) plus a hidden field _method with value LINK to something like /groups/{id}.
routing would be something like this:
group_invite:
path: /groups/{id}
defaults: { _controller: YourBundle:Group:inviteUser }
methods: [LINK]
You could use FOSRestBundle's implicit resource name definition, too.
For the method override to work the config setting framework.http_method_override needs to be set to true ( = default value - available since symfony version 2.3).
More information can be found in the documentation chapter:
How to use HTTP Methods beyond GET and POST in Routes