diff --git a/masquerade.module b/masquerade.module
index a487b60..2def8c9 100644
--- a/masquerade.module
+++ b/masquerade.module
@@ -23,7 +28,7 @@ function masquerade_help($path, $arg) {
  * @return array
  */
 function masquerade_permission() {
-  return array(
+  $perms = array(
     'masquerade as user' => array(
       'title' => t('Masquerade as user'),
       'description' => t('Masquerade as another user.'),
@@ -43,6 +48,17 @@ function masquerade_permission() {
       'description' => t('Perform administration tasks and configure the Masquerade module.'),
       'restrict access' => TRUE,
     ),
   );
+
+  // Allow fine grain role masquerading
+  $roles = user_roles();
+  foreach ($roles as $rid => $role) {
+    if($role == 'anonymous user') continue;
+    $perm_string = _masquerade_build_perm_string($role);
+    $perm_title = ucfirst($perm_string);
+    $perms[$perm_string] = array('title' => $perm_title, 'description' => $perm_title);
+  }
+
+  return $perms;
 }
 
@@ -241,6 +252,11 @@ function masquerade_menu_access($type, $uid = NULL) {
             ':uid_from' => $user->uid,
             ':uid_to' => $account->uid
           ))->fetchField();
+
+          // Unset switch to account if no access to at least one role
+          if(!_masquerade_user_access($account)) {
+            $switch_to_account = null;
+          }
         }
       }
       return !isset($_SESSION['masquerading']) && (user_access('masquerade as user') || user_access('masquerade as admin') || $switch_to_account);
@@ -567,6 +583,8 @@ function masquerade_block_1() {
     foreach ($masquerade_switches as $switch_user) {
       if (!isset($_SESSION['user']->uid) || $switch_user != $_SESSION['user']->uid) {
         $account = user_load($switch_user);
+        // Don't show quick switches if no access
+        if(!_masquerade_user_access($account)) continue;
         if (isset($account->uid)) {
           $switch_link = 'masquerade/switch/' . $account->uid;
           if ($account->uid) {
@@ -667,10 +685,12 @@ function masquerade_autocomplete($string) {
     $matches[$anonymous] = $anonymous;
   }
   // Other suggestions.
-  $result = db_query_range("SELECT name FROM {users} WHERE LOWER(name) LIKE LOWER(:string)", 0, 10, array(
+  $result = db_query_range("SELECT uid, name FROM {users} WHERE LOWER(name) LIKE LOWER(:string)", 0, 10, array(
     ':string' => $string . '%',
   ));
   foreach ($result as $user) {
+    $account = user_load($user->uid);
+    if(!_masquerade_user_access($account)) continue;
     $matches[$user->name] = check_plain($user->name);
   }
   if (module_exists('devel')) {
@@ -705,10 +725,12 @@ function masquerade_autocomplete_multiple($string, $add_anonymous = TRUE) {
       }
     }
     // Other suggestions.
-    $result = db_query_range("SELECT name FROM {users} WHERE LOWER(name) LIKE :string", 0, 10, array(
+    $result = db_query_range("SELECT uid, name FROM {users} WHERE LOWER(name) LIKE :string", 0, 10, array(
       ':string' => $last_string . '%',
     ));
     foreach ($result as $user) {
+      $account = user_load($user->uid);
+      if(!_masquerade_user_access($account)) continue;
       $matches[$prefix . $user->name] = check_plain($user->name);
     }
 
@@ -717,10 +739,12 @@ function masquerade_autocomplete_multiple($string, $add_anonymous = TRUE) {
 
     // @todo Check compatibility for D7.
     if (module_exists('alt_login')) {
-      $result = db_query_range("SELECT u.alt_login AS alt_login FROM {alt_login} u WHERE LOWER(u.alt_login) LIKE LOWER(:string)", 0, 10, array(
+      $result = db_query_range("SELECT u.uid, u.alt_login AS alt_login FROM {alt_login} u WHERE LOWER(u.alt_login) LIKE LOWER(:string)", 0, 10, array(
         ':string' => $last_string . '%',
       ));
       foreach ($result as $user) {
+        $account = user_load($user->uid);
+        if(!_masquerade_user_access($account)) continue;
         $matches[$user->alt_login] = check_plain($user->alt_login);
       }
     }
@@ -772,7 +796,7 @@ function masquerade_switch_user($uid) {
     ':uid_from' => $user->uid,
     ':uid_to' => $new_user->uid,
   ));
-  if (!user_access($perm) && !isset($_SESSION['masquerading']) && !$results->fetchField()) {
+  if ((!user_access($perm) || !_masquerade_user_access($new_user)) && !isset($_SESSION['masquerading']) && !$results->fetchField()) {
     watchdog('masquerade', 'This user requires administrative permissions to switch to the user %user.', array('%user' => $new_user->name), WATCHDOG_ERROR);
     return FALSE;
   }
@@ -860,3 +884,51 @@ function masquerade_switch_back() {
 
   watchdog('masquerade', 'User %user no longer masquerading as %masq_as.', array('%user' => $user->name, '%masq_as' => $oldname), WATCHDOG_INFO);
 }
+
+/**
+ * Generates a permission string for a given a role name.
+ */
+function _masquerade_build_perm_string($role_name) {
+  $perm = "masquerade as role {$role_name}";
+  return $perm;
+}
+
+/**
+ * Check that the current user has access to masquerade as the account roles
+ *
+ * @global type $user
+ * @param type $account
+ */
+function _masquerade_user_access($account) {
+  global $user;
+
+  // Return true right away if admin access
+  if(user_access('masquerade as admin')) return true;
+
+  /*
+   * @TODO
+   * Right now this assumes no hierarchy in roles and only allows masquerading access
+   * to $user if they have masquerade permissions to ALL the roles of $account.
+   *
+   * A better way to do this would be to create a roles hierarchy module to define
+   * a hierarchy of roles and limit access to the highest role permission the user has.
+   */
+  if(empty($account->roles)) {
+    $account = user_load($account->uid);
+  }
+
+  // Empty roles?!  weird.  no access for you!
+  if(empty($account->roles)) {
+    return false;
+  }
+
+  // return false on the first unmatched role
+  foreach($account->roles as $rid => $role) {
+    $permission = _masquerade_build_perm_string($role);
+    if(!(user_access($permission, $user))) {
+      return false;
+    }
+  }
+
+  return true;
+}
\ No newline at end of file
