1

I have some nodes which I want to hide from anonymous users when they access via a direct URL e.g. /node/1275.

However if someone accesses this via the URL alias which is an md5 hash I want to allow anonymous users to access it. This is so that the node is essentially available only to people I give the link to.

How would I achieve this? Ideally I'd like to avoid installing more modules.


EDIT:

Here's the complete solution that I used in my custom module. As well as the access check it contains a new filter type that generates a md5 for each node, which you can use in the URL alias patterns page.

 /**
 * Implements hook_menu_alter
 */
function newsletter_access_menu_alter(&$items){
  $items['node/%node']['access callback'] = 'newsletter_access_check_node_access';
}

/**
 * Custom menu access callback for node/%node
 */
function newsletter_access_check_node_access($op, $node){

  // Note $op and $node are passed in default node_menu access arguments
  if ($op == 'view' && isset($node->nid)) {
     if($node->type == "newsletter") {
            // Allow access to anon if request_uri contains specific hash
            if (!user_is_logged_in()) {
              $hash = md5(drupal_get_private_key() . $node->nid);
              // request_uri includes alias
              $request = explode('/', request_uri());
              if (in_array($hash, $request)) {
                return TRUE;
              } else {
                return FALSE;
              }
            }
        }
      }
  // Fallback to allow regular node_access checks
  return node_access($op, $node);
}


function newsletter_access_token_info() {
  $types['my_tokens'] = array(
    'name' => t("My Tokens"),
    'description' => t("My Token Category"),
  );

  $tokens['my_md5'] = array(
    'name' => t("Unique MD5 Hash"),
    'description' => t("The md5 hash unique for each node."),
  );

  return array(
    'types' => $types,
    'tokens' => array(
      'my_tokens' => $tokens,
    ),
  );

}

function newsletter_access_tokens($type, $tokens, array $data = array(), array $options = array()) {

  $replacements = array();

  if ($type == 'my_tokens') {
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'my_md5':
          $replacements[$original] =  md5(drupal_get_private_key() . $data['node']->nid);
        break;
      }
    }
  }

  return $replacements;
}

?>
  • looks good, the only thing I wonder about is making the hash a bit more secure. I added that MD5 one as a temporary example, but it would perhaps be better to use an additional salt paramter and/or perhaps a function like hash_hmac instead, otherwise fine. – David Thomas Jun 24 '13 at 23:20
  • Wrapping the $node->nid in an additional MD5 could suffice. – David Thomas Jun 24 '13 at 23:54
4

You could do something like

/**
 * Implements hook_menu_alter
 */
function MY_MODULE_menu_alter(&$items){
  $items['node/%node']['access callback'] = 'MY_MODULE_check_node_access';
}

/**
 * Custom menu access callback for node/%node
 */
function MY_MODULE_check_node_access($op, $node){
  // Note $op and $node are passed in default node_menu access arguments
  if ($op == 'view' && isset($node->nid)) {
    // Allow access to anon if request_uri contains specific hash
    if (!user_is_logged_in()) {
      $hash = md5(drupal_get_private_key() . md5($node->nid));
      // request_uri includes alias
      $request = explode('/', request_uri());
      if (in_array($hash, $request)) {
        return TRUE;
      } else {
        return FALSE;
      }
    }
  }
  // Fallback to allow regular node_access checks
  return node_access($op, $node);
}
  • 1
    that's a really flexible solution, and i could probably use that for a number of other permissions issues! i didn't know how to use access callbacks so it's good to know how to use it. – ankles Jun 22 '13 at 9:53
  • 1
    You're welcome, note I updated to add a condition for authenticated users to fallback to regular node_access. When you add a custom access callback, it's often a good idea to fallback to the original access callback if your conditions pass. – David Thomas Jun 22 '13 at 10:07
  • 1
    I think $items needs to be &$items to work (for other people's benefit this means the value get passed back) – ankles Jun 23 '13 at 22:27
  • @AlexLee thanks, I think you're right. It was written on the fly. Code adjusted. – David Thomas Jun 23 '13 at 22:48
  • hi if some one drupal_http_request /node/1275 will he able to retrieve node data? – Ali Nouman Jun 24 '13 at 7:43
0

Have you tried in the template.php? what if you do something like:

function yourtheme_preprocess_page(&$variables, $hook){
   if((arg(0) == 'node') && (!$user->uid)){
      //redirect
   }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.