anchor.diff:          _ ->
               true
       end.
  +
  +match_source([], _User, _PeerIP) ->
  +    {error, no_matching_sources};
  +match_source([{UserName, {IP,Mask}, Source, Options}|Tail], User, PeerIP) ->
  +    case (UserName == all orelse
  +          UserName == User) andalso
  +        mask_address(IP, Mask) == mask_address(PeerIP, Mask) of
  +        true ->
  +            {ok, Source, Options};
  +        false ->
  +            match_source(Tail, User, PeerIP)
  +    end.
  +
  +sort_sources(Sources) ->
  +    %% sort sources first by userlist, so that 'all' matches come last
  +    %% and then by CIDR, so that most sprcific masks come first
  +    Sources1 = lists:sort(fun({UserA, _, _, _}, {UserB, _, _, _}) ->
  +                    case {UserA, UserB} of
  +                        {all, all} ->
  +                            true;
  +                        {all, _} ->
  +                            %% anything is greater than 'all'
  +                            true;
  +                        {_, all} ->
  +                            false;
  +                        {_, _} ->
  +                            true
  +                    end
  +            end, Sources),
  +    lists:sort(fun({_, {_, MaskA}, _, _}, {_, {_, MaskB}, _, _}) ->
  +                MaskA > MaskB
  +        end, Sources1).
  +
  +%% group users sharing the same CIDR/Source/Options
  +group_sources(Sources) ->
  +    D = lists:foldl(fun({User, CIDR, Source, Options}, Acc) ->
  +                dict:append({CIDR, Source, Options}, User, Acc)
  +        end, dict:new(), Sources),
  +    R1 = [{Users, CIDR, Source, Options} || {{CIDR, Source, Options}, Users} <-
  +                                       dict:to_list(D)],
  +    %% sort the result by the same criteria that sort_sources uses
  +    R2 = lists:sort(fun({UserA, _, _, _}, {UserB, _, _, _}) ->
  +                    case {UserA, UserB} of
  +                        {[all], [all]} ->
  +                            true;
  +                        {[all], _} ->
  +                            %% anything is greater than 'all'
  +                            true;
  +                        {_, [all]} ->
  +                            false;
  +                        {_, _} ->
  +                            true
  +                    end
  +            end, R1),
  +    lists:sort(fun({_, {_, MaskA}, _, _}, {_, {_, MaskB}, _, _}) ->
  +                MaskA > MaskB
  +        end, R2).
  +
  +group_grants(Grants) ->
  +    D = lists:foldl(fun({{_User, Bucket}, G}, Acc) ->
  +                dict:append(Bucket, G, Acc)
  +        end, dict:new(), Grants),
  +    [{Bucket, lists:usort(flatten_once(P))} || {Bucket, P} <- dict:to_list(D)].
  +
  +flatten_once(List) ->
  +    lists:foldl(fun(A, Acc) ->
  +                        A ++ Acc
  +                end, [], List).
  +
  +
  
positive.code:     %% single user
      add_revoke([User], Bucket, Revokes).
  
  add_revoke_int([], _, _) ->
      ok;
  add_revoke_int([User|Users], Bucket, Permissions) ->
      UserGrants = riak_core_metadata:get({<<"security">>, <<"grants">>}, {User,
                                                                           Bucket}),
  
      %% check if there is currently a GRANT we can revoke
      case UserGrants of
          undefined ->
              %% can't REVOKE what wasn't GRANTED
              add_revoke_int(Users, Bucket, Permissions);
          GrantedPermissions ->
              NewPerms = [X || X <- GrantedPermissions, not lists:member(X,
                                                                         Permissions)],
  
              %% TODO - do deletes here, once cluster metadata supports it for
              %% real, if NeePerms == []
  
              riak_core_metadata:put({<<"security">>, <<"grants">>}, {User,
                                                                    Bucket},
                                     NewPerms),
              add_revoke_int(Users, Bucket, Permissions)
      end.
  
  add_source(all, CIDR, Source, Options) ->
      %% all is always valid
  
negative.code: add_source([H|_T]=UserList, CIDR, Source, Options) when is_binary(H) ->
      %% list of lists, weeeee
      %% validate the users...
      UnknownUsers = riak_core_metadata:fold(fun({Username, _}, Acc) ->
                                                     Acc -- [Username]
                                             end, UserList, {<<"security">>,
                                             <<"roles">>}),
  