whenever drupal needs to check if a current user has the right or not to see a specific node it passes through this process :
1. check if current user bypass node access
In determining access rights for a node, \Drupal\node\NodeAccessControlHandler
first checks whether the user has the "bypass node access" permission. Such users have unrestricted access to all nodes. user 1 (administrator) will always pass this check.

2.check Hook_node_access implementations
Next, all implementations of hook_node_access()
will be called. Each implementation may explicitly allow, explicitly forbid, or ignore the access request. If at least one module says to forbid the request, it will be rejected. If no modules deny the request and at least one says to allow it, the request will be permitted,
in the Example bellow whenever any user(except admin) tries to view a node we will check if field_prevent_webmaster is checked the user will get access forbidden message.
/**
* Implements hook_ENTITY_TYPE_access
*/
function modul_name_node_access(\Drupal\node\NodeInterface $node, $operation, AccountInterface $account) {
// 1.limit the action according to the operation performed on the requested entity
switch($operation){
case 'view': // when a user visit a node page ex : /node/52
// 2.limit the current operation according to the current account information
if( in_array('webmaster',$account->getRoles()) ){
//3. then further limit the current operation according to the current requested node entity field
// ex : if field_prevent_webmaster is checked prevent a user form
// viewing the node through the /node/{id}
if($node->hasField('field_prevent_webmaster') ) {
if ($node->field_prevent_webmaster->value == TRUE) {
return AccessResult::forbidden(); // send Access denied message to the client
}
}
if($node->hasField('field_allow_webmaster') ) {
if ($node->field_allow_webmaster->value == TRUE) {
return AccessResult::allowed(); // send Access denied message to the client
}
}
}
break;
case 'delete':// in /admin/content page u won't find the 'delete' button
if( in_array('webmaster',$account->getRoles()) ){
if($node->hasField('field_prevent_webmaster') ){
if( $node->field_prevent_webmaster->value == true){
return AccessResult::forbidden();
}
}
if($node->hasField('field_allow_webmaster') ) {
if ($node->field_allow_webmaster->value == TRUE) {
return AccessResult::allowed(); // send Access denied message to the client
}
}
}
break;
case 'update': // in /admin/content page u won't find the 'update' button
if( in_array('webmaster',$account->getRoles()) ){
if( $node->hasField('field_prevent_webmaster')){
if( $node->field_prevent_webmaster->value == true){
return AccessResult::forbidden();
}
}
if($node->hasField('field_allow_webmaster') ) {
if ($node->field_allow_webmaster->value == TRUE) {
return AccessResult::allowed();
}
}
}
break;
}
}
3.Getting permissions from node_access table
If all modules ignore the access request, then the node_access table is used to determine access and this more complicated then the previous 2 so we need to understand :
the node access table
- nid: The node id that will be affected by this permission line.
- gid(like user): the grant id, is simply a way to associate a user with a node.
- realm(like user type): It can be pretty much anything you want to consider it as a user type that groups a set of grant ids (gid).
- grant_view: Boolean indicating whether a user with the realm/grant pair can view this node.
- grant_update: Boolean indicating whether a user with the realm/grant pair can edit this node.
- grant_delete: Boolean indicating whether a user with the realm/grant pair can delete this node.
Example according to the node_access table bellow:
- the user that have gid=2 and realm=node_atuthor, he can view and update the node 52.
- the user that have gid=2 but a realm=node_super_user, he can view , update and delete the node 52.

Hook_node_access_records()
when a node is saved this hook will be executed, which enables us to fill the node_access table with the permission privileges that have already explore in the previous step.
/**
* Implements HOOK_node_access_records
**/
function entity_access_node_access_records(\Drupal\node\NodeInterface $node) {
$grants = [];
if( $node->isPublished() == TRUE) {
$grants = [
[
'realm' => 'node_author',
'gid' => $node->getOwnerId(),
'grant_view' => 1,
'grant_update' => 1,
'grant_delete' => 0,
'priority' => 0,
],
[
'realm' => 'node_viewer',
'gid' => $node->getOwnerId(),
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => 0,
],
[
'realm' => 'node_super_user',
'gid' => $node->getOwnerId(),
'grant_view' => 1,
'grant_update' => 1,
'grant_delete' => 1,
'priority' => 0,
]
];
}
return $grants;
}
Hook_node_grants()
is called every time access to a node is requested to determine access and this is what is used to check the node_access table and decide who what nodes the current user can see:
/**
* Implements Implements HOOK_node_grants
*/
function entity_access_node_grants(AccountInterface $account, $op) {
// Allow the user that have 'see_omar_is_nodes' permission and that is also not omar :
if ($account->hasPermission('see_omar_is_nodes') && $account->id() != 3){
// to get This gid realm grants ( to see his content + Omar is content)
$grants = [
'node_super_user' => [$account->id()],
'node_viewer' => [3]
] ;
}
// else if the user acount id =3 (omar)
else if ($account->id() == 3){
// get This gid realm grants of current user
$grants =[
'node_author' => [3]
] ;
}