diff --git flag.inc flag.inc
index 53b900c..280c526 100644
--- flag.inc
+++ flag.inc
@@ -407,6 +407,15 @@ class flag_flag {
     $matched_roles = array_intersect($this->roles[$action], array_keys($account->roles));
     return !empty($matched_roles) || $account->uid == 1;
   }
+  
+  /**
+   * Determine flag action by current flag status.
+   */
+  function get_action_by_status($content_id, $account) {
+    $uid = $account->uid;
+    $sid = flag_get_sid($uid);
+    return $this->is_flagged($content_id, $uid, $sid) ? 'unflag' : 'flag';
+  }
 
   /**
    * Returns TRUE if the user can flag, or unflag, the given content.
@@ -431,9 +440,7 @@ class flag_flag {
     }
 
     if (!isset($action)) {
-      $uid = $account->uid;
-      $sid = flag_get_sid($uid);
-      $action = $this->is_flagged($content_id, $uid, $sid) ? 'unflag' : 'flag';
+      $action = $this->get_action_by_status($content_id, $account); 
     }
 
     // Base initial access on the user's basic permission to use this flag.
@@ -1400,6 +1407,10 @@ class flag_user extends flag_flag {
     $options += array(
       'show_on_profile' => TRUE,
       'access_uid' => '',
+      'target_roles' => array (
+        'flag' => array (DRUPAL_AUTHENTICATED_RID),
+        'unflag' => array (DRUPAL_AUTHENTICATED_RID),
+      ),
     );
     return $options;
   }
@@ -1408,10 +1419,37 @@ class flag_user extends flag_flag {
     parent::options_form($form);
     $form['access']['types'] = array(
       // A user flag doesn't support node types.
-      // TODO: Maybe support roles instead of node types.
       '#type' => 'value',
       '#value' => array(0 => 0),
     );
+    
+    $form['access']['target_roles'] = array(
+      '#title' => t("Roles that can be flagged using this flag"),
+      '#access' => empty($this->locked['target_roles']),
+      '#description' => t("Users may only unflag content if they have access to flag the content initially. Checking <em>authenticated user</em> will allow to flag all logged-in users. Anonymous users can't be flagged."),
+      '#theme' => 'flag_form_roles',
+    );
+    // Sanitize user roles as recommended by Drupal security team.
+    // This is not a vulnerability fix but a sign of quality.  
+    $roles = array_map('check_plain', user_roles(TRUE)); 
+    $form['access']['target_roles']['flag'] = array(
+      '#type' => 'checkboxes',
+      '#options' => $roles,
+      '#default_value' => $this->target_roles['flag'],
+      '#parents' => array('target_roles', 'flag'),
+    );
+    $form['access']['target_roles']['unflag'] = array(
+      '#type' => 'checkboxes',
+      '#options' => $roles,
+      '#default_value' => $this->target_roles['unflag'],
+      '#parents' => array('target_roles', 'unflag'),
+    );
+    // Disabled access breaks checkboxes unless #value is hard coded.
+    if (!empty($this->locked['target_roles'])) {
+      $form['access']['target_roles']['#type'] = 'value';
+      $form['access']['target_roles']['#value'] = $this->target_roles;
+    }
+    
     $form['access']['access_uid'] = array(
       '#type' => 'checkbox',
       '#title' => t('Users may flag themselves'),
@@ -1428,6 +1466,10 @@ class flag_user extends flag_flag {
 
   function form_input($form_values) {
     parent::form_input($form_values);
+    // Same massaging as for $flag->roles.
+    $this->target_roles['flag'] = array_values(array_filter($this->target_roles['flag']));
+    $this->target_roles['unflag'] = array_values(array_filter($this->target_roles['unflag']));
+    
     // The access_uid value is intentionally backwards from the UI, to avoid
     // confusion caused by checking a box to disable a feature.
     $this->access_uid = empty($form_values['access_uid']) ? 'others' : '';
@@ -1446,10 +1488,37 @@ class flag_user extends flag_flag {
     return FALSE;
   }
 
+  /**
+   * TODO: We should define proper way of implementing access method because 
+   * order of checks is important.
+   * See http://drupal.org/node/720672
+   */
   function access($content_id, $action = NULL, $account = NULL) {
+    // Currently unless we override access method completely, we need to run
+    // this check too because we have no way to distinguish between denied
+    // flag and a flag that is not applicable.
+    // TODO: See http://drupal.org/node/720672
+    if (isset($content_id) && !$this->applies_to_content_id($content_id) ){
+      // Flag does not apply to this content.
+      return FALSE;
+    }    
     $access = parent::access($content_id, $action, $account);
     $account = isset($account) ? $account : $GLOBALS['user'];
-
+    
+    // We could just check parent::access() and if it's FALSE stop here,
+    // but we may need to allow modules to overrride our own checks, thus 
+    // we may need to continue checking.
+    if (!isset($action)) {
+      $action = $this->get_action_by_status($content_id, $account); 
+    }
+    
+    // Check if target user can be flagged depending on his roles
+    $content = $this->_load_content($content_id);
+    $matched_roles = array_intersect($this->target_roles[$action], array_keys($content->roles));
+    if (empty($matched_roles) && $account->uid != 1) {
+      return FALSE;
+    }
+    
     // Prevent users from flagging themselves.
     if ($this->access_uid == 'others' && $content_id == $account->uid) {
       $access = FALSE;
@@ -1466,6 +1535,22 @@ class flag_user extends flag_flag {
     if (array_key_exists(0, $access)) {
       $access[0] = FALSE;
     }
+    
+    // Check if target user can be flagged depending on his roles
+    //
+    // TODO: See http://drupal.org/node/720672
+    foreach ($content_ids as $content_id => $action) {
+      $roles = array(DRUPAL_AUTHENTICATED_RID);
+      // We could use IN (..) but that would make code more complicated.
+      $result = db_query('SELECT ur.rid FROM {users_roles} ur WHERE ur.uid = %d', $content_id);
+      while ($role = db_fetch_object($result)) {
+        $roles[] = $role->rid;
+      }
+      $matched_roles = array_intersect($this->target_roles[$action], $roles);
+      if (empty($matched_roles) && $account->uid != 1) {
+        $access[$content_id] = FALSE;
+      }
+    }
 
     // Prevent users from flagging themselves.
     if ($this->access_uid == 'others' && array_key_exists($account->uid, $access)) {
