Because RestController.php is a file and not one of your "mappings", the last rule will unconditionally serve a 403.
You need to either, make an exception for RestController.php and/or use the END flag (new in Apache 2.4), instead of L, to prevent all further processing of the mod_rewrite directives. (The L flag only stops the current round of processing. In a directory context, like .htaccess, the rewritten URL is passed to the next phase of processing and the last rule is then processed during the second phase.)
You also don't need an explicit rule to serve the 404, since that will happen by default. You only need a rule when the request maps to a file (or directory) in order to serve a 403.
For example, with an exception for RestController.php in the last rule block:
:
# catch all non-matched requests and throw 404 or 403 accordingly
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule !^RestController\.php$ - [R=403]
# A 404 will naturally be served otherwise...
You don't need the L flag when using a non-3xx status with the R flag.
You don't necessarily need to check for a directory (2nd condition), unless you happen to have a DirectoryIndex document (eg. index.php or index.html) in that directory (but why would you?). A 403 is served automatically by mod_autoindex when requesting a directory with no DirectoryIndex document.
Alternatively, or as well as, use the END flag in the preceding rules:
# REST mappings
RewriteRule ^json$ RestController.php?key=json [END,NC,QSA]
RewriteRule ^file/(.*) RestController.php?key=file&id=$1 [END,NC,QSA]
RewriteRule ^actions$ RestController.php?key=actions&id=none [END,NC,QSA]
RewriteRule ^actions/(.*) RestController.php?key=actions&id=$1 [END,NC,QSA]
# catch all non-matched requests and throw 404 or 403 accordingly
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [R=403]
# A 404 will naturally be served otherwise...
The NC flag should not be necessary here. Note also that I removed the $ in ^file/(.*)$ since it's not required (regex is greedy by default).
Aside:
# REST mappings
RewriteRule ^json$ RestController.php?key=json [END,NC,QSA]
RewriteRule ^file/(.*) RestController.php?key=file&id=$1 [END,NC,QSA]
RewriteRule ^actions$ RestController.php?key=actions&id=none [END,NC,QSA]
RewriteRule ^actions/(.*) RestController.php?key=actions&id=$1 [END,NC,QSA]
Your "mappings" could be reduced to a single rule if you standardise the endpoints, ie. Always a key and id parameter and id could be empty (not "none"), which you would need to validate for anyway (both /actions and /actions/ are valid requests according to your rules, but result in different targets). You then validate the target in your PHP script.
For example:
RewriteRule ^(json|file|actions)(?:/(.*))?$ RestController.php?key=$1&id=$2 [END,NC,QSA]
This does mean that /json/<foo> and /file would be successfully rewritten to RestController.php (which would otherwise be ignored by your original rules), but these requests should be handled by your REST API validation anyway. Perhaps .* should really be .+.