Index: includes/DefaultSettings.php
===================================================================
--- includes/DefaultSettings.php	(revision 23171)
+++ includes/DefaultSettings.php	(working copy)
@@ -1080,6 +1080,21 @@
  */
 # $wgGroupPermissions['developer']['siteadmin'] = true;
 
+// Limits user groups assignable and removable by every group
+$wgAssignableGroups['bureaucrat']['*'] = true;
+$wgRemovableGroups['bureaucrat']['*'] = true;
+
+// Settings suitable for wikimedia wikis
+//$wgAssignableGroups['steward'   ]['*'         ] = true;
+//$wgAssignableGroups['bureaucrat']['*'         ] = false;
+//$wgAssignableGroups['bureaucrat']['sysop'     ] = true;
+//$wgAssignableGroups['bureaucrat']['bureaucrat'] = true;
+//$wgAssignableGroups['bureaucrat']['bot'       ] = true;
+//$wgRemovableGroups['steward'   ]['*'          ] = true;
+//$wgRemovableGroups['bureaucrat']['*'          ] = false;
+//$wgRemovableGroups['bureaucrat']['bot'        ] = true;
+
+
 /**
  * Set of available actions that can be restricted via action=protect
  * You probably shouldn't change this.
Index: includes/HTMLForm.php
===================================================================
--- includes/HTMLForm.php	(revision 23171)
+++ includes/HTMLForm.php	(working copy)
@@ -121,6 +121,8 @@
  * @todo Document $selectmsg
 */
 function HTMLSelectGroups($selectname, $selectmsg, $selected=array(), $multiple=false, $size=6, $reverse=false) {
+	global $wgUser, $wgAssignableGroups, $wgRemovableGroups;
+	
 	$groups = User::getAllGroups();
 	$out = htmlspecialchars( wfMsg( $selectmsg ) );
 	$out .= "<br />";
@@ -148,6 +150,15 @@
 				$attribs['selected'] = 'selected';
 			}
 		}
+		
+		if( $selectname === 'member' ) {
+			$lookup = $wgRemovableGroups;
+		} else {
+			$lookup = $wgAssignableGroups;
+		}
+		if( !$wgUser->canChangeGroup( $lookup, $group ) ) {
+			$attribs['disabled'] = 'disabled';
+		}
 		$out .= wfElement( 'option', $attribs, User::getGroupName( $group ) ) . "\n";
 	}
 
Index: includes/SpecialUserrights.php
===================================================================
--- includes/SpecialUserrights.php	(revision 23171)
+++ includes/SpecialUserrights.php	(working copy)
@@ -75,7 +75,7 @@
 	 *
 	 */
 	function saveUserGroups( $username, $removegroup, $addgroup, $reason = '' ) {
-		global $wgOut;
+		global $wgOut, $wgAssignableGroups, $wgRemovableGroups;
 		$u = User::newFromName($username);
 
 		if(is_null($u)) {
@@ -90,21 +90,40 @@
 
 		$oldGroups = $u->getGroups();
 		$newGroups = $oldGroups;
-		// remove then add groups
+
+		// check if user can add or remove particular groups
 		if(isset($removegroup)) {
 			$newGroups = array_diff($newGroups, $removegroup);
 			foreach( $removegroup as $group ) {
-				$u->removeGroup( $group );
+				if( !$u->canChangeGroup( $wgRemovableGroups, $group ) ) {
+					$wgOut->addWikiText( wfMsg( 'userrights-nopermission', User::getGroupName( $group ) ) );
+					return;
+				}
 			}
 		}
 		if(isset($addgroup)) {
 			$newGroups = array_merge($newGroups, $addgroup);
 			foreach( $addgroup as $group ) {
-				$u->addGroup( $group );
+				if( !$u->canChangeGroup( $wgAssignableGroups, $group ) ) {
+					$wgOut->addWikiText( wfMsg( 'userrights-nopermission', User::getGroupName( $group ) ) );
+					return;
+				}
 			}
 		}
 		$newGroups = array_unique( $newGroups );
 
+		// remove then add groups
+		if(isset($removegroup)) {
+			foreach( $removegroup as $group ) {
+				$u->removeGroup( $group );
+			}
+		}
+		if(isset($addgroup)) {
+			foreach( $addgroup as $group ) {
+				$u->addGroup( $group );
+			}
+		}
+
 		wfDebug( 'oldGroups: ' . print_r( $oldGroups, true ) );
 		wfDebug( 'newGroups: ' . print_r( $newGroups, true ) );
 
Index: includes/User.php
===================================================================
--- includes/User.php	(revision 23171)
+++ includes/User.php	(working copy)
@@ -1745,6 +1745,21 @@
 	}
 
 	/**
+	 *  Check if user can add or remove a particular group
+	 * @param array $permissions Either $wgAssignableGroups or $wgRemovableGroups
+	 * @param string $group Group to be added or removed
+	 * @return bool True if user is allowed to do so, false otherwise
+	 */
+	function canChangeGroup( $permissions, $group )
+	{
+		foreach ( $this->mGroups as $right )
+		{
+			if ( $permissions[$right]['*'] || $permissions[$right][$group] ) return true;
+		}
+		return false;
+	}
+
+	/**
 	 * Load a skin if it doesn't exist or return it
 	 * @todo FIXME : need to check the old failback system [AV]
 	 */
Index: languages/messages/MessagesEn.php
===================================================================
--- languages/messages/MessagesEn.php	(revision 23171)
+++ languages/messages/MessagesEn.php	(working copy)
@@ -1277,6 +1277,7 @@
 'userrights-groupshelp'      => 'Select groups you want the user to be removed from or added to.
 Unselected groups will not be changed. You can deselect a group with CTRL + Left Click',
 'userrights-reason'          => 'Reason for change:',
+'userrights-nopermission'    => 'You don\'t have enough permissions to add or remove group "$1".',
 
 # Groups
 'group'            => 'Group:',
Index: languages/messages/MessagesRu.php
===================================================================
--- languages/messages/MessagesRu.php	(revision 23171)
+++ languages/messages/MessagesRu.php	(working copy)
@@ -1,4 +1,4 @@
-<?php
+﻿<?php
 /** Russian (русский язык)
   *
   * Based on MessagesEn.php revision 22305 (2007-05-22)
@@ -952,6 +952,7 @@
 'userrights-groupshelp'      => 'Выберите группы, в которые вы хотите включить или из которых хотите исключить участника.
 Невыбранные группы не изменятся. Снять выделение с группы можно используя CTRL + левую клавишу мыши.',
 'userrights-reason' => 'Причина изменения:',
+'userrights-nopermission'    => 'У вас недостаточно полномочий для присвоения или снятия группы «$1».',
 
 # Groups
 'group'            => 'Группа:',
Index: extensions/Makesysop/SpecialMakesysop.php
===================================================================
--- SpecialMakesysop.php	(revision 23011)
+++ SpecialMakesysop.php	(working copy)
@@ -27,11 +27,21 @@
 // * Bureaucrat can only do limited work (makesysop)
 $wgGroupPermissions['steward'   ]['makesysop' ] = true;
 $wgGroupPermissions['steward'   ]['userrights'] = true;
-$wgGroupPermissions['bureaucrat']['makesysop' ] = true;
-$wgGroupPermissions['bureaucrat']['userrights'] = false;
+$wgGroupPermissions['bureaucrat']['makesysop' ] = false;
+//$wgGroupPermissions['bureaucrat']['userrights'] = false;
 
 $wgAvailableRights[] = 'makesysop';
 
+$wgAssignableGroups['steward'   ]['*'         ] = true;
+$wgAssignableGroups['bureaucrat']['*'         ] = false;
+$wgAssignableGroups['bureaucrat']['sysop'     ] = true;
+$wgAssignableGroups['bureaucrat']['bureaucrat'] = true;
+$wgAssignableGroups['bureaucrat']['bot'       ] = true;
+
+$wgRemovableGroups['steward'   ]['*'          ] = true;
+$wgRemovableGroups['bureaucrat']['*'          ] = false;
+$wgRemovableGroups['bureaucrat']['bot'        ] = true;
+
 /**
  * Quick hack for clusters with multiple master servers; if an alternate
  * is listed for the requested database, a connection to it will be opened
  