Browse code

Make permissions more granular

Benjamin Roth authored on02/08/2023 22:20:40
Showing7 changed files
... ...
@@ -21,4 +21,5 @@ $GLOBALS['BE_MOD']['content']['modals'] = array
21 21
 $GLOBALS['TL_MODELS']['tl_vr_modal'] = ModalModel::class;
22 22
 
23 23
 // Add permissions
24
+$GLOBALS['TL_PERMISSIONS'][] = 'modals';
24 25
 $GLOBALS['TL_PERMISSIONS'][] = 'modalp';
... ...
@@ -15,12 +15,20 @@ use Contao\CoreBundle\DataContainer\PaletteManipulator;
15 15
 // Extend the default palette
16 16
 PaletteManipulator::create()
17 17
     ->addLegend('modal_legend', 'amg_legend', PaletteManipulator::POSITION_BEFORE)
18
-    ->addField(array('modalp'), 'modal_legend', PaletteManipulator::POSITION_APPEND)
18
+    ->addField(array('modals','modalp'), 'modal_legend', PaletteManipulator::POSITION_APPEND)
19 19
     ->applyToPalette('extend', 'tl_user')
20 20
     ->applyToPalette('custom', 'tl_user')
21 21
 ;
22 22
 
23 23
 // Add fields to tl_user_group
24
+$GLOBALS['TL_DCA']['tl_user']['fields']['modals'] = array
25
+(
26
+    'inputType'               => 'checkbox',
27
+    'foreignKey'              => 'tl_vr_modal.title',
28
+    'eval'                    => array('multiple'=>true),
29
+    'sql'                     => "blob NULL"
30
+);
31
+
24 32
 $GLOBALS['TL_DCA']['tl_user']['fields']['modalp'] = array
25 33
 (
26 34
     'exclude'                 => true,
... ...
@@ -15,11 +15,19 @@ use Contao\CoreBundle\DataContainer\PaletteManipulator;
15 15
 // Extend the default palette
16 16
 PaletteManipulator::create()
17 17
     ->addLegend('modal_legend', 'amg_legend', PaletteManipulator::POSITION_BEFORE)
18
-    ->addField(array('modalp'), 'modal_legend', PaletteManipulator::POSITION_APPEND)
18
+    ->addField(array('modals','modalp'), 'modal_legend', PaletteManipulator::POSITION_APPEND)
19 19
     ->applyToPalette('default', 'tl_user_group')
20 20
 ;
21 21
 
22 22
 // Add fields to tl_user_group
23
+$GLOBALS['TL_DCA']['tl_user_group']['fields']['modals'] = array
24
+(
25
+    'inputType'               => 'checkbox',
26
+    'foreignKey'              => 'tl_vr_modal.title',
27
+    'eval'                    => array('multiple'=>true),
28
+    'sql'                     => "blob NULL"
29
+);
30
+
23 31
 $GLOBALS['TL_DCA']['tl_user_group']['fields']['modalp'] = array
24 32
 (
25 33
     'exclude'                 => true,
... ...
@@ -13,6 +13,8 @@ declare(strict_types=1);
13 13
 use Contao\DC_Table;
14 14
 use Contao\DataContainer;
15 15
 
16
+$GLOBALS['TL_LANG']['tl_user']['modals'][0] = 'PopUps';
17
+$GLOBALS['TL_LANG']['tl_user']['modals'][1] = 'Hier können Sie den Zugriff auf ein oder mehrere PopUps erlauben.';
16 18
 $GLOBALS['TL_LANG']['tl_user']['modalp'][0] = 'PopUp-Rechte';
17 19
 $GLOBALS['TL_LANG']['tl_user']['modalp'][1] = 'Hier können Sie PopUp-Rechte festlegen.';
18 20
 
... ...
@@ -13,6 +13,8 @@ declare(strict_types=1);
13 13
 use Contao\DC_Table;
14 14
 use Contao\DataContainer;
15 15
 
16
+$GLOBALS['TL_LANG']['tl_user_group']['modals'][0] = 'PopUps';
17
+$GLOBALS['TL_LANG']['tl_user_group']['modals'][1] = 'Hier können Sie den Zugriff auf ein oder mehrere PopUps erlauben.';
16 18
 $GLOBALS['TL_LANG']['tl_user_group']['modalp'][0] = 'PopUp-Rechte';
17 19
 $GLOBALS['TL_LANG']['tl_user_group']['modalp'][1] = 'Hier können Sie PopUp-Rechte festlegen.';
18 20
 
... ...
@@ -20,6 +20,8 @@ use Contao\DataContainer;
20 20
 use Contao\Image;
21 21
 use Contao\Input;
22 22
 use Contao\StringUtil;
23
+use Contao\System;
24
+use Doctrine\DBAL\Connection;
23 25
 use Symfony\Component\HttpFoundation\Session\SessionInterface;
24 26
 use Symfony\Component\Security\Core\Security;
25 27
 use vonRotenberg\ModalBundle\Security\ModalPermissions;
... ...
@@ -43,11 +45,64 @@ class ModalDataContainerListener
43 45
         $user = $this->security->getUser();
44 46
         $userId = $user instanceof BackendUser ? (int)$user->id : 0;
45 47
 
48
+        $objSession = System::getContainer()->get('session');
49
+        $session = $objSession->all();
50
+
46 51
         if ($user->isAdmin)
47 52
         {
48 53
             return;
49 54
         }
50 55
 
56
+        // Set root IDs
57
+        if (empty($user->modals) || !is_array($user->modals))
58
+        {
59
+            $root = array(0);
60
+        }
61
+        else
62
+        {
63
+            $root = $user->modals;
64
+        }
65
+
66
+        if (is_array($session['CURRENT']['IDS'] ?? null))
67
+        {
68
+            $edit_all = array();
69
+            $delete_all = array();
70
+
71
+            foreach ($session['CURRENT']['IDS'] as $id)
72
+            {
73
+                if ($this->security->isGranted(ModalPermissions::USER_CAN_EDIT_MODALS, $id))
74
+                {
75
+                    $edit_all[] = $id;
76
+                }
77
+
78
+                if ($this->security->isGranted(ModalPermissions::USER_CAN_DELETE_MODALS, $id))
79
+                {
80
+                    $delete_all[] = $id;
81
+                }
82
+            }
83
+
84
+            $session['CURRENT']['IDS'] = (Input::get('act') == 'deleteAll') ? $delete_all : $edit_all;
85
+        }
86
+
87
+        // Set allowed clipboard IDs
88
+        if (!empty($session['CLIPBOARD']['tl_vr_modal']['id']) && is_array($session['CLIPBOARD']['tl_vr_modal']['id']))
89
+        {
90
+            $clipboard = array();
91
+
92
+            foreach ($session['CLIPBOARD']['tl_vr_modal']['id'] as $id)
93
+            {
94
+                if ($this->security->isGranted(ModalPermissions::USER_CAN_EDIT_MODALS, $id))
95
+                {
96
+                    $clipboard[] = $id;
97
+                }
98
+            }
99
+
100
+            $session['CLIPBOARD']['tl_article']['id'] = $clipboard;
101
+        }
102
+
103
+        // Overwrite the session
104
+        $objSession->replace($session);
105
+
51 106
         // Check permissions to add modals
52 107
         if (!$this->security->isGranted(ModalPermissions::USER_CAN_CREATE_MODALS))
53 108
         {
... ...
@@ -65,14 +120,27 @@ class ModalDataContainerListener
65 120
         // Check current action
66 121
         switch (Input::get('act'))
67 122
         {
68
-            case 'overrideAll':
69
-            case 'editAll':
70
-            case 'show':
71
-            case 'edit':
72 123
             case 'select':
73 124
                 // Allow
74 125
                 break;
75 126
 
127
+            case 'edit':
128
+            case 'show':
129
+                if (!in_array(Input::get('id'), $root))
130
+                {
131
+                    throw new AccessDeniedException('Not enough permissions to ' . Input::get('act') . ' form ID ' . Input::get('id') . '.');
132
+                }
133
+                break;
134
+
135
+            case 'editAll':
136
+            case 'overrideAll':
137
+                $session = $objSession->all();
138
+
139
+                $session['CURRENT']['IDS'] = array_intersect((array) $session['CURRENT']['IDS'], $root);
140
+
141
+                $objSession->replace($session);
142
+                break;
143
+
76 144
             case 'copy':
77 145
             case 'create':
78 146
                 if (!$this->security->isGranted(ModalPermissions::USER_CAN_CREATE_MODALS))
... ...
@@ -115,6 +183,95 @@ class ModalDataContainerListener
115 183
     }
116 184
 
117 185
     /**
186
+     * @Callback(table="tl_vr_modal", target="config.oncreate")
187
+     * @Callback(table="tl_vr_modal", target="config.oncopy")
188
+     */
189
+    public function adjustPermissions($insertId)
190
+    {
191
+        $user = $this->security->getUser();
192
+        $userId = $user instanceof BackendUser ? (int)$user->id : 0;
193
+
194
+        // The oncreate_callback passes $insertId as second argument
195
+        if (func_num_args() == 4)
196
+        {
197
+            $insertId = func_get_arg(1);
198
+        }
199
+
200
+        if ($user->isAdmin)
201
+        {
202
+            return;
203
+        }
204
+
205
+        // Set root IDs
206
+        if (empty($user->modals) || !is_array($user->modals))
207
+        {
208
+            $root = array(0);
209
+        }
210
+        else
211
+        {
212
+            $root = $user->modals;
213
+        }
214
+
215
+        // The form is enabled already
216
+        if (in_array($insertId, $root))
217
+        {
218
+            return;
219
+        }
220
+
221
+        /** @var AttributeBagInterface $objSessionBag */
222
+        $objSessionBag = System::getContainer()->get('session')->getBag('contao_backend');
223
+        $arrNew = $objSessionBag->get('new_records');
224
+
225
+        if (is_array($arrNew['tl_vr_modal']) && in_array($insertId, $arrNew['tl_vr_modal']))
226
+        {
227
+            /** @var Connection $db */
228
+            $db = System::getContainer()->get('database_connection');
229
+
230
+            // Add the permissions on group level
231
+            if ($user->inherit != 'custom')
232
+            {
233
+                $objGroup = $db->executeQuery("SELECT id, modals, modalp FROM tl_user_group WHERE id IN(" . implode(',', array_map('\intval', $user->groups)) . ")");
234
+
235
+                foreach ($objGroup->fetchAllAssociative() as $group)
236
+                {
237
+                    $arrModalp = StringUtil::deserialize($group['modalp']);
238
+
239
+                    if (is_array($arrModalp) && in_array('create', $arrModalp))
240
+                    {
241
+                        $arrModals = StringUtil::deserialize($group['modals'], true);
242
+                        $arrModals[] = $insertId;
243
+
244
+                        $db->prepare("UPDATE tl_user_group SET modals=? WHERE id=?")
245
+                            ->executeQuery([serialize($arrModals), $group['id']]);
246
+                    }
247
+                }
248
+            }
249
+
250
+            // Add the permissions on user level
251
+            if ($user->inherit != 'group')
252
+            {
253
+                $objUser = $db->prepare("SELECT modals, modalp FROM tl_user WHERE id=? LIMIT 1")
254
+                    ->executeQuery([$user->id]);
255
+
256
+                $arrModalp = StringUtil::deserialize($objUser->fetchOne());
257
+
258
+                if (is_array($arrModalp) && in_array('create', $arrModalp))
259
+                {
260
+                    $arrModals = StringUtil::deserialize($objUser->modals, true);
261
+                    $arrModals[] = $insertId;
262
+
263
+                    $db->prepare("UPDATE tl_user SET modals=? WHERE id=?")
264
+                        ->executeQuery([serialize($arrModals), $user->id]);
265
+                }
266
+            }
267
+
268
+            // Add the new element to the user object
269
+            $root[] = $insertId;
270
+            $user->forms = $root;
271
+        }
272
+    }
273
+
274
+    /**
118 275
      * @Callback(table="tl_vr_modal", target="list.operations.copy.button")
119 276
      */
120 277
     public function copyModal($row, $href, $label, $title, $icon, $attributes): string
... ...
@@ -129,4 +286,20 @@ class ModalDataContainerListener
129 286
     {
130 287
         return $this->security->isGranted(ModalPermissions::USER_CAN_DELETE_MODALS) ? '<a href="' . Backend::addToUrl($href . '&amp;id=' . $row['id']) . '" title="' . StringUtil::specialchars($title) . '"' . $attributes . '>' . Image::getHtml($icon, $label) . '</a> ' : Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)) . ' ';
131 288
     }
289
+
290
+    /**
291
+     * @Callback(table="tl_vr_modal", target="list.operations.edit.button")
292
+     */
293
+    public function editModal($row, $href, $label, $title, $icon, $attributes): string
294
+    {
295
+        return $this->security->isGranted(ModalPermissions::USER_CAN_EDIT_MODALS, $row['id']) ? '<a href="' . Backend::addToUrl($href . '&amp;id=' . $row['id']) . '" title="' . StringUtil::specialchars($title) . '"' . $attributes . '>' . Image::getHtml($icon, $label) . '</a> ' : Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)) . ' ';
296
+    }
297
+
298
+    /**
299
+     * @Callback(table="tl_vr_modal", target="list.operations.editheader.button")
300
+     */
301
+    public function editHeaderModal($row, $href, $label, $title, $icon, $attributes): string
302
+    {
303
+        return $this->security->isGranted(ModalPermissions::USER_CAN_EDIT_MODALS, $row['id']) ? '<a href="' . Backend::addToUrl($href . '&amp;id=' . $row['id']) . '" title="' . StringUtil::specialchars($title) . '"' . $attributes . '>' . Image::getHtml($icon, $label) . '</a> ' : Image::getHtml(preg_replace('/\.svg$/i', '_.svg', $icon)) . ' ';
304
+    }
132 305
 }
... ...
@@ -14,6 +14,7 @@ namespace vonRotenberg\ModalBundle\Security;
14 14
 
15 15
 final class ModalPermissions
16 16
 {
17
+    public const USER_CAN_EDIT_MODALS = 'contao_user.modals';
17 18
     public const USER_CAN_CREATE_MODALS = 'contao_user.modalp.create';
18 19
     public const USER_CAN_DELETE_MODALS = 'contao_user.modalp.delete';
19 20
 }