From b0dacc8eb06615cf5d0afb1fc8d799dd64325846 Mon Sep 17 00:00:00 2001
From: Vinnie Okada <vokada@mrvinn.com>
Date: Tue, 20 Jan 2015 20:34:09 -0700
Subject: [PATCH] Edit group members via API

Add an API endpoint to update the access level of an existing group
member.
---
 CHANGELOG                               |  2 +-
 doc/api/groups.md                       | 14 ++++++
 lib/api/group_members.rb                | 24 ++++++++++
 lib/api/helpers.rb                      |  5 ++
 lib/api/project_members.rb              | 12 +----
 spec/requests/api/group_members_spec.rb | 63 +++++++++++++++++++++++++
 6 files changed, 109 insertions(+), 11 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 286091afacf..52a41c7df3d 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -50,7 +50,7 @@ v 7.8.0 (unreleased)
   - 
   - Password reset token validity increased from 2 hours to 2 days since it is also send on account creation.
   - 
-  - 
+  - Edit group members via API
   - Enable raw image paste from clipboard, currently Chrome only (Marco Cyriacks)
   - 
   - 
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 9f01b550641..3c1858e697d 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -152,6 +152,20 @@ Parameters:
 - `user_id` (required) - The ID of a user to add
 - `access_level` (required) - Project access level
 
+### Edit group team member
+
+Updates a group team member to a specified access level.
+
+```
+PUT /groups/:id/members/:user_id
+```
+
+Parameters:
+
+- `id` (required) - The ID of a group
+- `user_id` (required) - The ID of a group member
+- `access_level` (required) - Project access level
+
 ### Remove user team member
 
 Removes user from user team.
diff --git a/lib/api/group_members.rb b/lib/api/group_members.rb
index 4373070083a..c9c9ccbcb2e 100644
--- a/lib/api/group_members.rb
+++ b/lib/api/group_members.rb
@@ -40,6 +40,30 @@ module API
         present member.user, with: Entities::GroupMember, group: group
       end
 
+      # Update group member
+      #
+      # Parameters:
+      #   id (required) - The ID of a group
+      #   user_id (required) - The ID of a group member
+      #   access_level (required) - Project access level
+      # Example Request:
+      #   PUT /groups/:id/members/:user_id
+      put ':id/members/:user_id' do
+        group = find_group(params[:id])
+        authorize! :manage_group, group
+        required_attributes! [:access_level]
+
+        team_member = group.group_members.find_by(user_id: params[:user_id])
+        not_found!('User can not be found') if team_member.nil?
+
+        if team_member.update_attributes(access_level: params[:access_level])
+          @member = team_member.user
+          present @member, with: Entities::GroupMember, group: group
+        else
+          handle_member_errors team_member.errors
+        end
+      end
+
       # Remove member.
       #
       # Parameters:
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 8fa30460ba6..a50ee4659a3 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -238,5 +238,10 @@ module API
     def secret_token
       File.read(Rails.root.join('.gitlab_shell_secret'))
     end
+
+    def handle_member_errors(errors)
+      error!(errors[:access_level], 422) if errors[:access_level].any?
+      not_found!(errors)
+    end
   end
 end
diff --git a/lib/api/project_members.rb b/lib/api/project_members.rb
index 1e890f9e199..73cf062155b 100644
--- a/lib/api/project_members.rb
+++ b/lib/api/project_members.rb
@@ -4,14 +4,6 @@ module API
     before { authenticate! }
 
     resource :projects do
-      helpers do
-        def handle_project_member_errors(errors)
-          if errors[:access_level].any?
-            error!(errors[:access_level], 422)
-          end
-          not_found!(errors)
-        end
-      end
 
       # Get a project team members
       #
@@ -66,7 +58,7 @@ module API
           @member = team_member.user
           present @member, with: Entities::ProjectMember, project: user_project
         else
-          handle_project_member_errors team_member.errors
+          handle_member_errors team_member.errors
         end
       end
 
@@ -89,7 +81,7 @@ module API
           @member = team_member.user
           present @member, with: Entities::ProjectMember, project: user_project
         else
-          handle_project_member_errors team_member.errors
+          handle_member_errors team_member.errors
         end
       end
 
diff --git a/spec/requests/api/group_members_spec.rb b/spec/requests/api/group_members_spec.rb
index 4957186f605..43d26d67efe 100644
--- a/spec/requests/api/group_members_spec.rb
+++ b/spec/requests/api/group_members_spec.rb
@@ -104,6 +104,69 @@ describe API::API, api: true  do
     end
   end
 
+  describe 'PUT /groups/:id/members/:user_id' do
+    context 'when not a member of the group' do
+      it 'should return a 409 error if the user is not a group member' do
+        put(
+          api("/groups/#{group_no_members.id}/members/#{developer.id}",
+              owner), access_level: GroupMember::MASTER
+        )
+        expect(response.status).to eq(404)
+      end
+    end
+
+    context 'when a member of the group' do
+      it 'should return ok and update member access level' do
+        put(
+          api("/groups/#{group_with_members.id}/members/#{reporter.id}",
+              owner),
+          access_level: GroupMember::MASTER
+        )
+
+        expect(response.status).to eq(200)
+
+        get api("/groups/#{group_with_members.id}/members", owner)
+        json_reporter = json_response.find do |e|
+          e['id'] == reporter.id
+        end
+
+        expect(json_reporter['access_level']).to eq(GroupMember::MASTER)
+      end
+
+      it 'should not allow guest to modify group members' do
+        put(
+          api("/groups/#{group_with_members.id}/members/#{developer.id}",
+              guest),
+          access_level: GroupMember::MASTER
+        )
+
+        expect(response.status).to eq(403)
+
+        get api("/groups/#{group_with_members.id}/members", owner)
+        json_developer = json_response.find do |e|
+          e['id'] == developer.id
+        end
+
+        expect(json_developer['access_level']).to eq(GroupMember::DEVELOPER)
+      end
+
+      it 'should return a 400 error when access level is not given' do
+        put(
+          api("/groups/#{group_with_members.id}/members/#{master.id}", owner)
+        )
+        expect(response.status).to eq(400)
+      end
+
+      it 'should return a 422 error when access level is not known' do
+        put(
+          api("/groups/#{group_with_members.id}/members/#{master.id}", owner),
+          access_level: 1234
+        )
+        expect(response.status).to eq(422)
+      end
+    end
+  end
+
   describe "DELETE /groups/:id/members/:user_id" do
     context "when not a member of the group" do
       it "should not delete guest's membership of group_with_members" do
-- 
GitLab