From 67522fc64649b2adc2709c855420ae90c2574c0b Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 26 Oct 2016 12:51:47 +0100 Subject: [PATCH 01/41] Override LDAP members permissions Closes #343 --- app/assets/javascripts/members.js.es6 | 21 ++++++++++++++++++++ app/assets/stylesheets/pages/members.scss | 21 ++++++++++++++++++++ app/policies/group_member_policy.rb | 2 ++ app/policies/group_policy.rb | 2 +- app/views/shared/members/_member.html.haml | 23 +++++++++++++++++++++- 5 files changed, 67 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 895bc10784ff5f..bda40a79fa627f 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -8,6 +8,12 @@ } addListeners() { + const ldapPermissionsChangeBtns = document.querySelectorAll('.js-ldap-permissions'); + + ldapPermissionsChangeBtns.forEach((btn) => { + btn.addEventListener('click', this.showLDAPPermissionsWarning.bind(this)); + }); + $('.project_member, .group_member').off('ajax:success').on('ajax:success', this.removeRow); $('.js-member-update-control').off('change').on('change', this.formSubmit); $('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess); @@ -32,6 +38,21 @@ formSuccess() { $(this).find('.js-member-update-control').enable(); } + + showLDAPPermissionsWarning (e) { + const btn = e.currentTarget, + ldapPermissionsElement = this.getLDAPPermissionsElement(btn); + + if (ldapPermissionsElement.style.display === 'none') { + ldapPermissionsElement.style.display = 'block'; + } else { + ldapPermissionsElement.style.display = 'none'; + } + } + + getLDAPPermissionsElement (btn) { + return document.getElementById(btn.dataset.id).nextElementSibling; + } } gl.Members = Members; diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index 756efa9c7fa027..a9e8621a66aebc 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -96,3 +96,24 @@ border: 0; outline: 0; } + +.members-ldap { + -webkit-align-self: center; + align-self: center; + height: 100%; + margin-right: 10px; + margin-left: -49px; +} + +.alert-member-ldap { + background-color: #fff1e0; + + > p { + float: left; + color: $orange-normal; + + @media (min-width: $screen-sm-min) { + padding-left: 55px; + } + } +} diff --git a/app/policies/group_member_policy.rb b/app/policies/group_member_policy.rb index 62335527654cd2..bc7abe33c8e907 100644 --- a/app/policies/group_member_policy.rb +++ b/app/policies/group_member_policy.rb @@ -15,5 +15,7 @@ def rules elsif @user == target_user can! :destroy_group_member end + + cannot! :update_group_member if @subject.ldap end end diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index cced89212001d3..39bfb64dd11dc1 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -35,7 +35,7 @@ def rules end # EE-only - cannot! :admin_group_member if @subject.ldap_synced? + # cannot! :admin_group_member if @subject.ldap_synced? end def can_read_group? diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index e67f7d5a352e65..01b17388ccb698 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -45,6 +45,9 @@ = time_ago_with_tooltip(member.created_at) - if show_roles .controls.member-controls + - if member.ldap + %span.label.label-info.members-ldap + LDAP - if show_controls && (member.respond_to?(:group) && @group) || (member.respond_to?(:project) && @project) - if user != current_user = form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f| @@ -66,7 +69,7 @@ class: 'btn btn-success prepend-left-10', title: 'Grant access' - - if can?(current_user, action_member_permission(:destroy, member), member) + - if can?(current_user, action_member_permission(:destroy, member), member) && !member.ldap - if current_user == user = link_to icon('sign-out', text: 'Leave'), polymorphic_path([:leave, member.source, :members]), method: :delete, @@ -82,5 +85,23 @@ %span.visible-xs-block Delete = icon('trash', class: 'hidden-xs') + - elsif member.ldap + %button.btn.btn-default.prepend-left-10.js-ldap-permissions{ type: "button", + "aria-label" => "Override LDAP settings", + data: { name: user.name, id: dom_id(member) } } + = icon("pencil") - else %span.member-access-text= member.human_access +- if member.ldap + %li.alert.alert-member-ldap{ style: "display: none;" } + %p + = user.name + is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync. + .controls + %button.btn.btn-warning{ type: "button", + "aria-label" => "Change LDAP member permissions" } + Change permissions + %button.btn.btn-default.js-ldap-permissions{ type: "button", + "aria-label" => "Close permissions override", + data: { id: dom_id(member) } } + = icon("times") -- GitLab From a046d9c1fa70ffc36bafe527f95ac3fea5ffd203 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 27 Oct 2016 08:44:26 +0100 Subject: [PATCH 02/41] Changed to GL dropdown - not working 100% --- app/assets/javascripts/members.js.es6 | 11 ++++++++--- app/assets/stylesheets/pages/members.scss | 4 ++++ app/views/shared/members/_member.html.haml | 23 +++++++++++++++++++++- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index bda40a79fa627f..81566a420fbd83 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -41,7 +41,8 @@ showLDAPPermissionsWarning (e) { const btn = e.currentTarget, - ldapPermissionsElement = this.getLDAPPermissionsElement(btn); + memberListItem = this.getMemberListItem(btn), + ldapPermissionsElement = memberListItem.nextElementSibling; if (ldapPermissionsElement.style.display === 'none') { ldapPermissionsElement.style.display = 'block'; @@ -50,8 +51,12 @@ } } - getLDAPPermissionsElement (btn) { - return document.getElementById(btn.dataset.id).nextElementSibling; + getMemberListItem (btn) { + return document.getElementById(btn.dataset.id); + } + + toggleMemberAccessToggle (el) { + const toggle = el.querySelectorAll('.dropdown-menu-toggle')[0]; } } diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index a9e8621a66aebc..f909e85be4c501 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -54,6 +54,10 @@ @media (min-width: $screen-sm-min) { width: 50%; } + + .dropdown-menu-toggle { + width: 100%; + } } .member-access-text { diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index 01b17388ccb698..5c4899a8f0e41a 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -51,7 +51,28 @@ - if show_controls && (member.respond_to?(:group) && @group) || (member.respond_to?(:project) && @project) - if user != current_user = form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f| - = f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control member-form-control append-right-5 js-member-update-control', id: "member_access_level_#{member.id}", disabled: !can_admin_member + -# = f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control member-form-control append-right-5 js-member-update-control', id: "member_access_level_#{member.id}", disabled: !can_admin_member + .member-form-control.dropdown.append-right-5 + %button.dropdown-menu-toggle{ type: "button", + disabled: member.ldap && !member.override, + data: { toggle: "dropdown" } } + = member.human_access + = icon("caret-down") + .dropdown-menu.dropdown-select.dropdown-menu-align-right.dropdown-menu-selectable + = dropdown_title("Change permissions") + .dropdown-content + %ul + - Gitlab::Access.options.each do |role, role_id| + %li + %a{ href: "#", + class: ("is-active" if member.access_level == role_id), + data: { id: role_id } } + = role + - if member.ldap + %li.divider + %li + %a{ href: "#" } + Revert to LDAP group sync settings .prepend-left-5.clearable-input.member-form-control = f.text_field :expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: 'Expiration date', id: "member_expires_at_#{member.id}", disabled: !can_admin_member %i.clear-icon.js-clear-input -- GitLab From ea76b6bc1ccb3bf933c65268ed04945096e2bff1 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 27 Oct 2016 13:34:55 +0100 Subject: [PATCH 03/41] Updates override value in DB --- app/assets/javascripts/members.js.es6 | 64 +++++++++++++++++-- app/assets/stylesheets/pages/members.scss | 6 ++ .../groups/group_members_controller.rb | 3 +- app/policies/group_member_policy.rb | 2 +- app/views/shared/members/_member.html.haml | 16 +++-- 5 files changed, 77 insertions(+), 14 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 81566a420fbd83..f3136bb93cefce 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -5,21 +5,50 @@ class Members { constructor() { this.addListeners(); + this.initGLDropdown(); } addListeners() { const ldapPermissionsChangeBtns = document.querySelectorAll('.js-ldap-permissions'); + const ldapOverrideBtns = document.querySelectorAll('.js-ldap-override'); ldapPermissionsChangeBtns.forEach((btn) => { btn.addEventListener('click', this.showLDAPPermissionsWarning.bind(this)); }); + ldapOverrideBtns.forEach((btn) => { + btn.addEventListener('click', this.toggleMemberAccessToggle.bind(this)); + }); + $('.project_member, .group_member').off('ajax:success').on('ajax:success', this.removeRow); $('.js-member-update-control').off('change').on('change', this.formSubmit); $('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess); gl.utils.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change'); } + initGLDropdown () { + $('.js-member-permissions-dropdown').each((i, btn) => { + const $btn = $(btn); + + $btn.glDropdown({ + selectable: true, + fieldName: 'test', + id () { + return 1; + }, + clicked: (selected, $el) => { + const $link = $($el); + + if ($link.data('revert')) { + const memberListitem = this.getMemberListItem($link.get(0)); + + this.overrideLdap(memberListitem, $link.data('endpoint'), false); + } + } + }); + }); + } + removeRow(e) { const $target = $(e.target); @@ -40,9 +69,9 @@ } showLDAPPermissionsWarning (e) { - const btn = e.currentTarget, - memberListItem = this.getMemberListItem(btn), - ldapPermissionsElement = memberListItem.nextElementSibling; + const btn = e.currentTarget; + const memberListItem = this.getMemberListItem(btn); + const ldapPermissionsElement = memberListItem.nextElementSibling; if (ldapPermissionsElement.style.display === 'none') { ldapPermissionsElement.style.display = 'block'; @@ -55,8 +84,33 @@ return document.getElementById(btn.dataset.id); } - toggleMemberAccessToggle (el) { - const toggle = el.querySelectorAll('.dropdown-menu-toggle')[0]; + toggleMemberAccessToggle (e) { + const btn = e.currentTarget; + const memberListItem = this.getMemberListItem(btn); + const toggle = memberListItem.querySelectorAll('.dropdown-menu-toggle')[0]; + + this.showLDAPPermissionsWarning(e); + toggle.removeAttribute('disabled'); + + this.overrideLdap(memberListItem, btn.dataset.endpoint, true); + } + + overrideLdap (memberListitem, endpoint, override) { + if (override) { + memberListitem.classList.add('is-overriden'); + } else { + memberListitem.classList.remove('is-overriden'); + } + + return $.ajax({ + url: endpoint, + type: 'PATCH', + data: { + group_member: { + override + } + } + }) } } diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index f909e85be4c501..fa185c32ef4ca9 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -4,6 +4,12 @@ } .member { + &.is-overriden { + .btn-ldap-override { + visibility: hidden; + } + } + .list-item-name { @media (min-width: $screen-sm-min) { float: left; diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index fa251813c3297e..b747991b4b41f5 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -43,6 +43,7 @@ def create def update @group_member = @group.group_members.find(params[:id]) + puts @group_member return render_403 unless can?(current_user, :update_group_member, @group_member) @@ -81,7 +82,7 @@ def resend_invite protected def member_params - params.require(:group_member).permit(:access_level, :user_id, :expires_at) + params.require(:group_member).permit(:access_level, :user_id, :expires_at, :override) end # MembershipActions concern diff --git a/app/policies/group_member_policy.rb b/app/policies/group_member_policy.rb index bc7abe33c8e907..e18016628ac231 100644 --- a/app/policies/group_member_policy.rb +++ b/app/policies/group_member_policy.rb @@ -16,6 +16,6 @@ def rules can! :destroy_group_member end - cannot! :update_group_member if @subject.ldap + # cannot! :update_group_member if @subject.ldap end end diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index 5c4899a8f0e41a..5410e4a3bcbd4e 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -4,7 +4,7 @@ - source = member.source - can_admin_member = can?(current_user, action_member_permission(:update, member), member) -%li.member{ class: dom_class(member), id: dom_id(member) } +%li.member{ class: [dom_class(member), ("is-overriden" if member.override)], id: dom_id(member) } %span.list-item-name - if user = image_tag avatar_icon(user, 40), class: "avatar s40", alt: '' @@ -53,7 +53,7 @@ = form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f| -# = f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control member-form-control append-right-5 js-member-update-control', id: "member_access_level_#{member.id}", disabled: !can_admin_member .member-form-control.dropdown.append-right-5 - %button.dropdown-menu-toggle{ type: "button", + %button.dropdown-menu-toggle.js-member-permissions-dropdown{ type: "button", disabled: member.ldap && !member.override, data: { toggle: "dropdown" } } = member.human_access @@ -64,14 +64,15 @@ %ul - Gitlab::Access.options.each do |role, role_id| %li - %a{ href: "#", + %a{ href: "javascript:void(0)", class: ("is-active" if member.access_level == role_id), data: { id: role_id } } = role - if member.ldap %li.divider %li - %a{ href: "#" } + %a{ href: "javascript:void(0)", + data: { revert: "true", endpoint: group_group_member_path(@group, member), id: dom_id(member) } } Revert to LDAP group sync settings .prepend-left-5.clearable-input.member-form-control = f.text_field :expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: 'Expiration date', id: "member_expires_at_#{member.id}", disabled: !can_admin_member @@ -107,7 +108,7 @@ Delete = icon('trash', class: 'hidden-xs') - elsif member.ldap - %button.btn.btn-default.prepend-left-10.js-ldap-permissions{ type: "button", + %button.btn.btn-default.btn-ldap-override.prepend-left-10.js-ldap-permissions{ type: "button", "aria-label" => "Override LDAP settings", data: { name: user.name, id: dom_id(member) } } = icon("pencil") @@ -119,8 +120,9 @@ = user.name is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync. .controls - %button.btn.btn-warning{ type: "button", - "aria-label" => "Change LDAP member permissions" } + %button.btn.btn-warning.js-ldap-override{ type: "button", + "aria-label" => "Change LDAP member permissions", + data: { id: dom_id(member), endpoint: group_group_member_path(@group, member) } } Change permissions %button.btn.btn-default.js-ldap-permissions{ type: "button", "aria-label" => "Close permissions override", -- GitLab From e39cc88f3b60e8ae58bfbf080a3c23f21836a441 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 27 Oct 2016 16:57:25 +0100 Subject: [PATCH 04/41] Use GL dropdowns to update group members --- app/assets/javascripts/members.js.es6 | 18 +++++++++++++----- app/assets/stylesheets/pages/members.scss | 1 + app/views/shared/members/_member.html.haml | 12 +++++++++--- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index f3136bb93cefce..f18e3d86b5d1bd 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -32,17 +32,25 @@ $btn.glDropdown({ selectable: true, - fieldName: 'test', - id () { - return 1; + fieldName: $btn.data('field-name'), + id (selected, $el) { + return $el.data('id'); + }, + toggleLabel (selected, $el) { + console.log($el.text().trim()); + return $el.text(); }, clicked: (selected, $el) => { const $link = $($el); if ($link.data('revert')) { - const memberListitem = this.getMemberListItem($link.get(0)); + const memberListItem = this.getMemberListItem($link.get(0)); + const toggle = memberListItem.querySelectorAll('.dropdown-menu-toggle')[0]; - this.overrideLdap(memberListitem, $link.data('endpoint'), false); + toggle.disabled = true; + this.overrideLdap(memberListItem, $link.data('endpoint'), false); + } else { + $btn.closest('form').trigger("submit.rails"); } } }); diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index fa185c32ef4ca9..bc763d62481c0c 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -117,6 +117,7 @@ .alert-member-ldap { background-color: #fff1e0; + line-height: 40px; > p { float: left; diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index 5410e4a3bcbd4e..2cb36530679513 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -34,6 +34,10 @@ %span{ class: ('text-warning' if member.expires_soon?) } Expires in #{distance_of_time_in_words_to_now(member.expires_at)} + - if member.ldap + %span.label.label-info.pull-right.visible-xs-block + LDAP + - else = image_tag avatar_icon(member.invite_email, 40), class: "avatar s40", alt: '' %strong= member.invite_email @@ -46,17 +50,19 @@ - if show_roles .controls.member-controls - if member.ldap - %span.label.label-info.members-ldap + %span.label.label-info.members-ldap.hidden-xs LDAP - if show_controls && (member.respond_to?(:group) && @group) || (member.respond_to?(:project) && @project) - if user != current_user = form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f| + = f.hidden_field :access_level -# = f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control member-form-control append-right-5 js-member-update-control', id: "member_access_level_#{member.id}", disabled: !can_admin_member .member-form-control.dropdown.append-right-5 %button.dropdown-menu-toggle.js-member-permissions-dropdown{ type: "button", disabled: member.ldap && !member.override, - data: { toggle: "dropdown" } } - = member.human_access + data: { toggle: "dropdown", field_name: "#{f.object_name}[access_level]" } } + %span.dropdown-toggle-text + = member.human_access = icon("caret-down") .dropdown-menu.dropdown-select.dropdown-menu-align-right.dropdown-menu-selectable = dropdown_title("Change permissions") -- GitLab From 5b0a041e8c4bfce869748f027e0b7b1f2760ba9a Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 27 Oct 2016 17:01:11 +0100 Subject: [PATCH 05/41] Update URL to work with project member --- app/views/shared/members/_member.html.haml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index 2cb36530679513..e2ba69caf05d42 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -3,6 +3,7 @@ - user = local_assigns.fetch(:user, member.user) - source = member.source - can_admin_member = can?(current_user, action_member_permission(:update, member), member) +- update_url = member.type == 'GroupMember' ? group_group_member_path(@group, member) : namespace_project_project_member_path(@project.namespace, @project, member) %li.member{ class: [dom_class(member), ("is-overriden" if member.override)], id: dom_id(member) } %span.list-item-name @@ -78,7 +79,7 @@ %li.divider %li %a{ href: "javascript:void(0)", - data: { revert: "true", endpoint: group_group_member_path(@group, member), id: dom_id(member) } } + data: { revert: "true", endpoint: update_url, id: dom_id(member) } } Revert to LDAP group sync settings .prepend-left-5.clearable-input.member-form-control = f.text_field :expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: 'Expiration date', id: "member_expires_at_#{member.id}", disabled: !can_admin_member @@ -128,7 +129,7 @@ .controls %button.btn.btn-warning.js-ldap-override{ type: "button", "aria-label" => "Change LDAP member permissions", - data: { id: dom_id(member), endpoint: group_group_member_path(@group, member) } } + data: { id: dom_id(member), endpoint: update_url } } Change permissions %button.btn.btn-default.js-ldap-permissions{ type: "button", "aria-label" => "Close permissions override", -- GitLab From e213112f0352add6c092eed39a560def0760b75c Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 28 Oct 2016 15:14:59 +0100 Subject: [PATCH 06/41] Mobile spacing for override ldap permissions box Toggle button text fix --- app/assets/javascripts/members.js.es6 | 7 +++-- app/assets/stylesheets/pages/members.scss | 30 ++++++++++++++++++++-- app/views/shared/members/_member.html.haml | 8 +++--- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index f18e3d86b5d1bd..154e70965c3a51 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -37,8 +37,11 @@ return $el.data('id'); }, toggleLabel (selected, $el) { - console.log($el.text().trim()); - return $el.text(); + if ($link.data('revert')) { + return $btn.text(); + } else { + return $el.text(); + } }, clicked: (selected, $el) => { const $link = $($el); diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index bc763d62481c0c..681f33cafd6af3 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -6,7 +6,11 @@ .member { &.is-overriden { .btn-ldap-override { - visibility: hidden; + display: none!important; + } + + .form-horizontal { + margin-right: 45px; } } @@ -117,14 +121,36 @@ .alert-member-ldap { background-color: #fff1e0; - line-height: 40px; + + @media (min-width: $screen-sm-min) { + line-height: 40px; + } > p { float: left; + margin-bottom: 10px; color: $orange-normal; @media (min-width: $screen-sm-min) { padding-left: 55px; + margin-bottom: 0; + } + } + + .controls { + width: 100%; + + @media (min-width: $screen-sm-min) { + width: auto; } } } + +.btn-ldap-override { + width: 100%; + + @media (min-width: $screen-sm-min) { + margin-left: 10px; + width: auto; + } +} diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index e2ba69caf05d42..51cce7ba60b8e9 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -115,10 +115,12 @@ Delete = icon('trash', class: 'hidden-xs') - elsif member.ldap - %button.btn.btn-default.btn-ldap-override.prepend-left-10.js-ldap-permissions{ type: "button", - "aria-label" => "Override LDAP settings", + %button.btn.btn-default.btn-ldap-override.js-ldap-permissions{ type: "button", + "aria-label" => "Edit permissions", data: { name: user.name, id: dom_id(member) } } - = icon("pencil") + %span.visible-xs-block.visible-sm-block + Edit permissions + = icon("pencil", class: "hidden-xs hidden-sm") - else %span.member-access-text= member.human_access - if member.ldap -- GitLab From fc7286a09ffafafbee67dbae01d851ca4b7cef30 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 28 Oct 2016 15:18:12 +0100 Subject: [PATCH 07/41] Fixed JS error --- app/assets/javascripts/members.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 154e70965c3a51..cf33fd67ea9714 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -37,7 +37,7 @@ return $el.data('id'); }, toggleLabel (selected, $el) { - if ($link.data('revert')) { + if ($el.data('revert')) { return $btn.text(); } else { return $el.text(); -- GitLab From 403c91db2db3d000de77623d34ea354f4086d4f0 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 28 Oct 2016 15:40:21 +0100 Subject: [PATCH 08/41] Stops 'revert' link in dropdown being selectable --- app/assets/javascripts/gl_dropdown.js | 5 +++++ app/assets/javascripts/members.js.es6 | 5 +++++ app/assets/stylesheets/pages/members.scss | 4 +++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 969778dded7d99..9a91018a8e4647 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -650,6 +650,11 @@ } else if(value) { field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value.toString().replace(/'/g, '\\\'') + "']"); } + + if (this.options.isSelectable && !this.options.isSelectable(selectedObject, el)) { + return; + } + if (el.hasClass(ACTIVE_CLASS)) { el.removeClass(ACTIVE_CLASS); if (field && field.length) { diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index cf33fd67ea9714..5d6c63ee452bcd 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -32,6 +32,11 @@ $btn.glDropdown({ selectable: true, + isSelectable (selected, $el) { + const $link = $($el); + + return $link.data('revert') ? false : true; + }, fieldName: $btn.data('field-name'), id (selected, $el) { return $el.data('id'); diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index 681f33cafd6af3..3d815e3f5e7c24 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -10,7 +10,9 @@ } .form-horizontal { - margin-right: 45px; + @media (min-width: $screen-sm-min) { + margin-right: 45px; + } } } -- GitLab From 877ddab2ade10635eff94c1c1e98a978ae96b31a Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 24 Nov 2016 15:00:08 +0000 Subject: [PATCH 09/41] Fixed bug with not updating times when changes Enabled eslint --- app/assets/javascripts/members.js.es6 | 48 +++++++++---------- app/views/groups/group_members/update.js.haml | 1 + .../projects/project_members/update.js.haml | 1 + 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 5d6c63ee452bcd..4b3cd8700e0c4f 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -1,6 +1,5 @@ -/* eslint-disable */ -((w) => { - w.gl = w.gl || {}; +(() => { + window.gl = window.gl || {}; class Members { constructor() { @@ -26,27 +25,27 @@ gl.utils.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change'); } - initGLDropdown () { + initGLDropdown() { $('.js-member-permissions-dropdown').each((i, btn) => { const $btn = $(btn); $btn.glDropdown({ selectable: true, - isSelectable (selected, $el) { + isSelectable(selected, $el) { const $link = $($el); - return $link.data('revert') ? false : true; + return $link.data('revert'); }, fieldName: $btn.data('field-name'), - id (selected, $el) { + id(selected, $el) { return $el.data('id'); }, - toggleLabel (selected, $el) { + toggleLabel(selected, $el) { if ($el.data('revert')) { return $btn.text(); - } else { - return $el.text(); } + + return $el.text(); }, clicked: (selected, $el) => { const $link = $($el); @@ -58,33 +57,34 @@ toggle.disabled = true; this.overrideLdap(memberListItem, $link.data('endpoint'), false); } else { - $btn.closest('form').trigger("submit.rails"); + $btn.closest('form').trigger('submit.rails'); } - } + }, }); }); } - removeRow(e) { + static removeRow(e) { const $target = $(e.target); if ($target.hasClass('btn-remove')) { $target.closest('.member') - .fadeOut(function () { + .fadeOut(function fadeOutMemberRow() { $(this).remove(); }); } } formSubmit() { - $(this).closest('form').trigger("submit.rails").end().disable(); + $(this).closest('form').trigger('submit.rails').end() + .disable(); } formSuccess() { $(this).find('.js-member-update-control').enable(); } - showLDAPPermissionsWarning (e) { + showLDAPPermissionsWarning(e) { const btn = e.currentTarget; const memberListItem = this.getMemberListItem(btn); const ldapPermissionsElement = memberListItem.nextElementSibling; @@ -96,11 +96,11 @@ } } - getMemberListItem (btn) { + static getMemberListItem(btn) { return document.getElementById(btn.dataset.id); } - toggleMemberAccessToggle (e) { + toggleMemberAccessToggle(e) { const btn = e.currentTarget; const memberListItem = this.getMemberListItem(btn); const toggle = memberListItem.querySelectorAll('.dropdown-menu-toggle')[0]; @@ -111,7 +111,7 @@ this.overrideLdap(memberListItem, btn.dataset.endpoint, true); } - overrideLdap (memberListitem, endpoint, override) { + static overrideLdap(memberListitem, endpoint, override) { if (override) { memberListitem.classList.add('is-overriden'); } else { @@ -123,12 +123,12 @@ type: 'PATCH', data: { group_member: { - override - } - } - }) + override, + }, + }, + }); } } gl.Members = Members; -})(window); +})(); diff --git a/app/views/groups/group_members/update.js.haml b/app/views/groups/group_members/update.js.haml index de8f53b6b52ba2..3e450b69561be5 100644 --- a/app/views/groups/group_members/update.js.haml +++ b/app/views/groups/group_members/update.js.haml @@ -1,3 +1,4 @@ :plain var $listItem = $('#{escape_javascript(render('shared/members/member', member: @group_member))}'); $("##{dom_id(@group_member)} .list-item-name").replaceWith($listItem.find('.list-item-name')); + gl.utils.localTimeAgo($('.js-timeago')); diff --git a/app/views/projects/project_members/update.js.haml b/app/views/projects/project_members/update.js.haml index 91927181efbeba..ab36dd5ef18cca 100644 --- a/app/views/projects/project_members/update.js.haml +++ b/app/views/projects/project_members/update.js.haml @@ -1,3 +1,4 @@ :plain var $listItem = $('#{escape_javascript(render('shared/members/member', member: @project_member))}'); $("##{dom_id(@project_member)} .list-item-name").replaceWith($listItem.find('.list-item-name')); + gl.utils.timeAgo($('.js-timeago')); -- GitLab From 3efb7139b4b7e0cf6bafc4926d8c886c6160a236 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 24 Nov 2016 15:03:29 +0000 Subject: [PATCH 10/41] Fixed static methods --- app/assets/javascripts/members.js.es6 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 4b3cd8700e0c4f..732b0527523aef 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -1,3 +1,4 @@ +/* eslint-disable class-methods-use-this */ (() => { window.gl = window.gl || {}; @@ -96,7 +97,7 @@ } } - static getMemberListItem(btn) { + getMemberListItem(btn) { return document.getElementById(btn.dataset.id); } @@ -111,7 +112,7 @@ this.overrideLdap(memberListItem, btn.dataset.endpoint, true); } - static overrideLdap(memberListitem, endpoint, override) { + overrideLdap(memberListitem, endpoint, override) { if (override) { memberListitem.classList.add('is-overriden'); } else { -- GitLab From 25b494cfad57f219ad8d9c2ca9c41ca13df1cc0a Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 25 Nov 2016 08:43:16 +0000 Subject: [PATCH 11/41] Added disabled styling Fixes active items being selectable --- app/assets/javascripts/members.js.es6 | 2 +- app/assets/stylesheets/framework/dropdowns.scss | 5 +++++ app/views/shared/members/_member.html.haml | 13 +++++-------- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 732b0527523aef..4a2a215fab4c29 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -35,7 +35,7 @@ isSelectable(selected, $el) { const $link = $($el); - return $link.data('revert'); + return $link.data('revert') || $link.hasClass('.is-active'); }, fieldName: $btn.data('field-name'), id(selected, $el) { diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 55d1b117abb090..aef5d0c66094c3 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -42,6 +42,11 @@ border-radius: $border-radius-base; white-space: nowrap; + &[disabled] { + background-color: $input-bg-disabled; + cursor: not-allowed; + } + &.no-outline { outline: 0; } diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index 51cce7ba60b8e9..51703be4897215 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -57,10 +57,9 @@ - if user != current_user = form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f| = f.hidden_field :access_level - -# = f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control member-form-control append-right-5 js-member-update-control', id: "member_access_level_#{member.id}", disabled: !can_admin_member .member-form-control.dropdown.append-right-5 %button.dropdown-menu-toggle.js-member-permissions-dropdown{ type: "button", - disabled: member.ldap && !member.override, + disabled: !can_admin_member, data: { toggle: "dropdown", field_name: "#{f.object_name}[access_level]" } } %span.dropdown-toggle-text = member.human_access @@ -71,16 +70,14 @@ %ul - Gitlab::Access.options.each do |role, role_id| %li - %a{ href: "javascript:void(0)", + = link_to role, "javascript:void(0)", class: ("is-active" if member.access_level == role_id), - data: { id: role_id } } - = role + data: { id: role_id } - if member.ldap %li.divider %li - %a{ href: "javascript:void(0)", - data: { revert: "true", endpoint: update_url, id: dom_id(member) } } - Revert to LDAP group sync settings + = link_to "Revert to LDAP group sync settings", "javascript:void(0)", + data: { revert: "true", endpoint: update_url, id: dom_id(member) } .prepend-left-5.clearable-input.member-form-control = f.text_field :expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: 'Expiration date', id: "member_expires_at_#{member.id}", disabled: !can_admin_member %i.clear-icon.js-clear-input -- GitLab From 49086108090eabff9fd10529021d06cae5343a87 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 25 Nov 2016 09:03:15 +0000 Subject: [PATCH 12/41] Fixed dropdown not changing when selected --- app/assets/javascripts/members.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 4a2a215fab4c29..637200a38a6281 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -35,7 +35,7 @@ isSelectable(selected, $el) { const $link = $($el); - return $link.data('revert') || $link.hasClass('.is-active'); + return $link.data('revert') || !$link.hasClass('is-active'); }, fieldName: $btn.data('field-name'), id(selected, $el) { -- GitLab From a8b8799d8aa5f880b9c1b30b59dc241d35d44b0c Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 25 Nov 2016 09:40:03 +0000 Subject: [PATCH 13/41] Removed random puts code --- app/controllers/groups/group_members_controller.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index b747991b4b41f5..30c4d7aa8cba31 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -43,7 +43,6 @@ def create def update @group_member = @group.group_members.find(params[:id]) - puts @group_member return render_403 unless can?(current_user, :update_group_member, @group_member) -- GitLab From ba4572ca3535a8c7b6d3fb33096d27e9c07cd029 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 25 Nov 2016 10:14:16 +0000 Subject: [PATCH 14/41] fixed JS to work with phantomjs --- app/assets/javascripts/members.js.es6 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 637200a38a6281..c22800eec9d5de 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -12,13 +12,15 @@ const ldapPermissionsChangeBtns = document.querySelectorAll('.js-ldap-permissions'); const ldapOverrideBtns = document.querySelectorAll('.js-ldap-override'); - ldapPermissionsChangeBtns.forEach((btn) => { + for (let i = 0, changeBtnLength = ldapPermissionsChangeBtns.length; i < changeBtnLength; i++) { + const btn = ldapPermissionsChangeBtns[i]; btn.addEventListener('click', this.showLDAPPermissionsWarning.bind(this)); - }); + } - ldapOverrideBtns.forEach((btn) => { + for (let i = 0, overrideBtnLength = ldapOverrideBtns.length; i < overrideBtnLength; i++) { + const btn = ldapOverrideBtns[i]; btn.addEventListener('click', this.toggleMemberAccessToggle.bind(this)); - }); + } $('.project_member, .group_member').off('ajax:success').on('ajax:success', this.removeRow); $('.js-member-update-control').off('change').on('change', this.formSubmit); -- GitLab From 10d8e1e2b77e61b065ad6b646454ac247e233948 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 25 Nov 2016 11:57:19 +0000 Subject: [PATCH 15/41] Updated tests to use new dropdowns --- app/assets/javascripts/members.js.es6 | 6 ++++-- features/steps/group/members.rb | 7 ++++++- features/steps/project/team_management.rb | 6 +++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index c22800eec9d5de..10a27f8d9bb493 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -10,14 +10,16 @@ addListeners() { const ldapPermissionsChangeBtns = document.querySelectorAll('.js-ldap-permissions'); + const changeBtnLength = ldapPermissionsChangeBtns.length; const ldapOverrideBtns = document.querySelectorAll('.js-ldap-override'); + const overrideBtnLength = ldapOverrideBtns.length; - for (let i = 0, changeBtnLength = ldapPermissionsChangeBtns.length; i < changeBtnLength; i++) { + for (let i = 0; i < changeBtnLength; i += 1) { const btn = ldapPermissionsChangeBtns[i]; btn.addEventListener('click', this.showLDAPPermissionsWarning.bind(this)); } - for (let i = 0, overrideBtnLength = ldapOverrideBtns.length; i < overrideBtnLength; i++) { + for (let i = 0; i < overrideBtnLength; i += 1) { const btn = ldapOverrideBtns[i]; btn.addEventListener('click', this.toggleMemberAccessToggle.bind(this)); } diff --git a/features/steps/group/members.rb b/features/steps/group/members.rb index cefc55d07abef1..adaf375453cfb5 100644 --- a/features/steps/group/members.rb +++ b/features/steps/group/members.rb @@ -117,7 +117,12 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps member = mary_jane_member page.within "#group_member_#{member.id}" do - select 'Developer', from: "member_access_level_#{member.id}" + click_button member.human_access + + page.within '.dropdown-menu' do + click_link 'Developer' + end + wait_for_ajax end end diff --git a/features/steps/project/team_management.rb b/features/steps/project/team_management.rb index b21d0849ad1fc2..99b6397ba74100 100644 --- a/features/steps/project/team_management.rb +++ b/features/steps/project/team_management.rb @@ -65,7 +65,11 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps user = User.find_by(name: 'Dmitriy') project_member = project.project_members.find_by(user_id: user.id) page.within "#project_member_#{project_member.id}" do - select "Reporter", from: "member_access_level_#{project_member.id}" + click_button project_member.human_access + + page.within '.dropdown-menu' do + click_link 'Reporter' + end end end -- GitLab From b08ea890e062df7ff5c0ba88d54a2c35a9655abf Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 25 Nov 2016 13:29:34 +0000 Subject: [PATCH 16/41] Fixed row not removing --- app/assets/javascripts/members.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 10a27f8d9bb493..696655cbd2a31b 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -69,7 +69,7 @@ }); } - static removeRow(e) { + removeRow(e) { const $target = $(e.target); if ($target.hasClass('btn-remove')) { -- GitLab From 2bcb903529fba6cdb1c93409b5287c83ef4e6dd2 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 29 Nov 2016 09:18:45 +0000 Subject: [PATCH 17/41] Fixed date input not disabling --- app/assets/javascripts/members.js.es6 | 4 ++++ app/views/shared/members/_member.html.haml | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 696655cbd2a31b..0ac8165c29dd57 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -58,8 +58,10 @@ if ($link.data('revert')) { const memberListItem = this.getMemberListItem($link.get(0)); const toggle = memberListItem.querySelectorAll('.dropdown-menu-toggle')[0]; + const dateInput = memberListItem.querySelectorAll('.js-access-expiration-date')[0]; toggle.disabled = true; + dateInput.disabled = true; this.overrideLdap(memberListItem, $link.data('endpoint'), false); } else { $btn.closest('form').trigger('submit.rails'); @@ -109,9 +111,11 @@ const btn = e.currentTarget; const memberListItem = this.getMemberListItem(btn); const toggle = memberListItem.querySelectorAll('.dropdown-menu-toggle')[0]; + const dateInput = memberListItem.querySelectorAll('.js-access-expiration-date')[0]; this.showLDAPPermissionsWarning(e); toggle.removeAttribute('disabled'); + dateInput.removeAttribute('disabled'); this.overrideLdap(memberListItem, btn.dataset.endpoint, true); } diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index 51703be4897215..648305e92dcb9b 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -5,7 +5,7 @@ - can_admin_member = can?(current_user, action_member_permission(:update, member), member) - update_url = member.type == 'GroupMember' ? group_group_member_path(@group, member) : namespace_project_project_member_path(@project.namespace, @project, member) -%li.member{ class: [dom_class(member), ("is-overriden" if member.override)], id: dom_id(member) } +%li.member{ class: [dom_class(member), ("is-overriden" if member.override && can_admin_member)], id: dom_id(member) } %span.list-item-name - if user = image_tag avatar_icon(user, 40), class: "avatar s40", alt: '' @@ -111,7 +111,7 @@ %span.visible-xs-block Delete = icon('trash', class: 'hidden-xs') - - elsif member.ldap + - elsif member.ldap && can_admin_member %button.btn.btn-default.btn-ldap-override.js-ldap-permissions{ type: "button", "aria-label" => "Edit permissions", data: { name: user.name, id: dom_id(member) } } @@ -120,7 +120,7 @@ = icon("pencil", class: "hidden-xs hidden-sm") - else %span.member-access-text= member.human_access -- if member.ldap +- if member.ldap && can_admin_member %li.alert.alert-member-ldap{ style: "display: none;" } %p = user.name -- GitLab From e0014e3db02378d40b63edca34c04a13d0a9f4f4 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 6 Dec 2016 17:17:32 +0000 Subject: [PATCH 18/41] Refactored with jQuery to make the code nicer & manageable --- app/assets/javascripts/members.js.es6 | 77 +++++++++------------ app/assets/stylesheets/framework/lists.scss | 6 ++ app/views/shared/members/_member.html.haml | 2 +- 3 files changed, 38 insertions(+), 47 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 0ac8165c29dd57..7024a3dfb3b293 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -9,21 +9,8 @@ } addListeners() { - const ldapPermissionsChangeBtns = document.querySelectorAll('.js-ldap-permissions'); - const changeBtnLength = ldapPermissionsChangeBtns.length; - const ldapOverrideBtns = document.querySelectorAll('.js-ldap-override'); - const overrideBtnLength = ldapOverrideBtns.length; - - for (let i = 0; i < changeBtnLength; i += 1) { - const btn = ldapPermissionsChangeBtns[i]; - btn.addEventListener('click', this.showLDAPPermissionsWarning.bind(this)); - } - - for (let i = 0; i < overrideBtnLength; i += 1) { - const btn = ldapOverrideBtns[i]; - btn.addEventListener('click', this.toggleMemberAccessToggle.bind(this)); - } - + $('.js-ldap-permissions').off('click').on('click', this.showLDAPPermissionsWarning.bind(this)); + $('.js-ldap-override').off('click').on('click', this.toggleMemberAccessToggle.bind(this)); $('.project_member, .group_member').off('ajax:success').on('ajax:success', this.removeRow); $('.js-member-update-control').off('change').on('change', this.formSubmit); $('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess); @@ -39,7 +26,11 @@ isSelectable(selected, $el) { const $link = $($el); - return $link.data('revert') || !$link.hasClass('is-active'); + if ($link.data('revert')) { + return false; + } + + return !$link.hasClass('is-active'); }, fieldName: $btn.data('field-name'), id(selected, $el) { @@ -56,13 +47,11 @@ const $link = $($el); if ($link.data('revert')) { - const memberListItem = this.getMemberListItem($link.get(0)); - const toggle = memberListItem.querySelectorAll('.dropdown-menu-toggle')[0]; - const dateInput = memberListItem.querySelectorAll('.js-access-expiration-date')[0]; + const { $memberListItem, $toggle, $dateInput } = this.getMemberListItems($link); - toggle.disabled = true; - dateInput.disabled = true; - this.overrideLdap(memberListItem, $link.data('endpoint'), false); + $toggle.attr('disabled', true); + $dateInput.attr('disabled', true); + this.overrideLdap($memberListItem, $link.data('endpoint'), false); } else { $btn.closest('form').trigger('submit.rails'); } @@ -92,40 +81,36 @@ } showLDAPPermissionsWarning(e) { - const btn = e.currentTarget; - const memberListItem = this.getMemberListItem(btn); - const ldapPermissionsElement = memberListItem.nextElementSibling; - - if (ldapPermissionsElement.style.display === 'none') { - ldapPermissionsElement.style.display = 'block'; - } else { - ldapPermissionsElement.style.display = 'none'; - } + const $btn = $(e.currentTarget); + const { $memberListItem } = this.getMemberListItems($btn); + const $ldapPermissionsElement = $memberListItem.next(); + + $ldapPermissionsElement.toggle(); } - getMemberListItem(btn) { - return document.getElementById(btn.dataset.id); + getMemberListItems(btn) { + const $memberListItem = $(`#${btn.data('id')}`); + + return { + $memberListItem, + $toggle: $memberListItem.find('.dropdown-menu-toggle'), + $dateInput: $memberListItem.find('.js-access-expiration-date'), + }; } toggleMemberAccessToggle(e) { - const btn = e.currentTarget; - const memberListItem = this.getMemberListItem(btn); - const toggle = memberListItem.querySelectorAll('.dropdown-menu-toggle')[0]; - const dateInput = memberListItem.querySelectorAll('.js-access-expiration-date')[0]; + const $btn = $(e.currentTarget); + const { $memberListItem, $toggle, $dateInput } = this.getMemberListItems($btn); this.showLDAPPermissionsWarning(e); - toggle.removeAttribute('disabled'); - dateInput.removeAttribute('disabled'); + $toggle.removeAttr('disabled'); + $dateInput.removeAttr('disabled'); - this.overrideLdap(memberListItem, btn.dataset.endpoint, true); + this.overrideLdap($memberListItem, $btn.data('endpoint'), true); } - overrideLdap(memberListitem, endpoint, override) { - if (override) { - memberListitem.classList.add('is-overriden'); - } else { - memberListitem.classList.remove('is-overriden'); - } + overrideLdap($memberListitem, endpoint, override) { + $memberListitem.toggleClass('is-overriden', override); return $.ajax({ url: endpoint, diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index ed4b60faf92c88..8409ce179eb874 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -161,6 +161,12 @@ ul.content-list { margin-top: 3px; margin-bottom: 4px; + &.btn-ldap-override { + @media (min-width: $screen-sm-min) { + margin-bottom: 0; + } + } + &:last-child { margin-right: 0; } diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index 648305e92dcb9b..8859d176fe669c 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -63,7 +63,7 @@ data: { toggle: "dropdown", field_name: "#{f.object_name}[access_level]" } } %span.dropdown-toggle-text = member.human_access - = icon("caret-down") + = icon("chevron-down") .dropdown-menu.dropdown-select.dropdown-menu-align-right.dropdown-menu-selectable = dropdown_title("Change permissions") .dropdown-content -- GitLab From 059eb92fe907c085418e403aa0b3bf03328cf7c1 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 6 Dec 2016 16:31:50 -0200 Subject: [PATCH 19/41] Use the `ldap?` predicated method --- app/views/shared/members/_member.html.haml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index 8859d176fe669c..209d84beebd463 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -35,7 +35,7 @@ %span{ class: ('text-warning' if member.expires_soon?) } Expires in #{distance_of_time_in_words_to_now(member.expires_at)} - - if member.ldap + - if member.ldap? %span.label.label-info.pull-right.visible-xs-block LDAP @@ -50,7 +50,7 @@ = time_ago_with_tooltip(member.created_at) - if show_roles .controls.member-controls - - if member.ldap + - if member.ldap? %span.label.label-info.members-ldap.hidden-xs LDAP - if show_controls && (member.respond_to?(:group) && @group) || (member.respond_to?(:project) && @project) @@ -73,7 +73,7 @@ = link_to role, "javascript:void(0)", class: ("is-active" if member.access_level == role_id), data: { id: role_id } - - if member.ldap + - if member.ldap? %li.divider %li = link_to "Revert to LDAP group sync settings", "javascript:void(0)", @@ -95,7 +95,7 @@ class: 'btn btn-success prepend-left-10', title: 'Grant access' - - if can?(current_user, action_member_permission(:destroy, member), member) && !member.ldap + - if can?(current_user, action_member_permission(:destroy, member), member) && !member.ldap? - if current_user == user = link_to icon('sign-out', text: 'Leave'), polymorphic_path([:leave, member.source, :members]), method: :delete, @@ -111,7 +111,7 @@ %span.visible-xs-block Delete = icon('trash', class: 'hidden-xs') - - elsif member.ldap && can_admin_member + - elsif member.ldap? && can_admin_member %button.btn.btn-default.btn-ldap-override.js-ldap-permissions{ type: "button", "aria-label" => "Edit permissions", data: { name: user.name, id: dom_id(member) } } @@ -120,7 +120,7 @@ = icon("pencil", class: "hidden-xs hidden-sm") - else %span.member-access-text= member.human_access -- if member.ldap && can_admin_member +- if member.ldap? && can_admin_member %li.alert.alert-member-ldap{ style: "display: none;" } %p = user.name -- GitLab From cc65fb54612daa75f52a489add43d896d13102a6 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 6 Dec 2016 19:34:44 -0200 Subject: [PATCH 20/41] Add an action to update the override attribute on the group membership --- app/controllers/groups/group_members_controller.rb | 14 ++++++++++++++ app/views/shared/members/_member.html.haml | 4 ++-- config/routes/group.rb | 4 ++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 30c4d7aa8cba31..5ebdabab6943f1 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -78,6 +78,20 @@ def resend_invite end end + def override + @group_member = @group.group_members.find(params[:id]) + + return render_403 unless can?(current_user, :override_group_member, @group_member) + + if @group_member.update_attributes(override_params) + log_audit_event(@group_member, action: :override) + + respond_to do |format| + format.js { head :ok } + end + end + end + protected def member_params diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index 209d84beebd463..ea2a5bb578affb 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -77,7 +77,7 @@ %li.divider %li = link_to "Revert to LDAP group sync settings", "javascript:void(0)", - data: { revert: "true", endpoint: update_url, id: dom_id(member) } + data: { revert: "true", endpoint: override_group_group_member_path(@group, member), id: dom_id(member) } .prepend-left-5.clearable-input.member-form-control = f.text_field :expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: 'Expiration date', id: "member_expires_at_#{member.id}", disabled: !can_admin_member %i.clear-icon.js-clear-input @@ -128,7 +128,7 @@ .controls %button.btn.btn-warning.js-ldap-override{ type: "button", "aria-label" => "Change LDAP member permissions", - data: { id: dom_id(member), endpoint: update_url } } + data: { id: dom_id(member), endpoint: override_group_group_member_path(@group, member) } } Change permissions %button.btn.btn-default.js-ldap-permissions{ type: "button", "aria-label" => "Close permissions override", diff --git a/config/routes/group.rb b/config/routes/group.rb index 7d0b36b22db6be..e280d84fb382f5 100644 --- a/config/routes/group.rb +++ b/config/routes/group.rb @@ -18,6 +18,10 @@ resources :group_members, only: [:index, :create, :update, :destroy], concerns: :access_requestable do post :resend_invite, on: :member delete :leave, on: :collection + + ## EE-specific + patch :override, on: :member + ## EE-specific end resource :avatar, only: [:destroy] -- GitLab From 26f32aff9bf93b73277047312f036ea3e1b02496 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 6 Dec 2016 19:38:20 -0200 Subject: [PATCH 21/41] Allow owner/master to change membership when LDAP group sync is enabled --- .../groups/group_members_controller.rb | 15 +++++++++++++-- app/policies/group_member_policy.rb | 8 +++++++- app/policies/group_policy.rb | 5 ++++- app/views/shared/members/_member.html.haml | 15 ++++++++------- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 5ebdabab6943f1..698bc283b783fb 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -2,7 +2,8 @@ class Groups::GroupMembersController < Groups::ApplicationController include MembershipActions # Authorize - before_action :authorize_admin_group_member!, except: [:index, :leave, :request_access] + before_action :authorize_admin_group_member!, except: [:index, :leave, :request_access, :update, :override] + before_action :authorize_update_group_member!, only: [:update, :override] def index @project = @group.projects.find(params[:project_id]) if params[:project_id] @@ -94,8 +95,18 @@ def override protected + def authorize_update_group_member! + unless can?(current_user, :admin_group_member, group) || can?(current_user, :override_group_member, group) + return render_403 + end + end + def member_params - params.require(:group_member).permit(:access_level, :user_id, :expires_at, :override) + params.require(:group_member).permit(:access_level, :user_id, :expires_at) + end + + def override_params + params.require(:group_member).permit(:override) end # MembershipActions concern diff --git a/app/policies/group_member_policy.rb b/app/policies/group_member_policy.rb index e18016628ac231..7b45b283bfd986 100644 --- a/app/policies/group_member_policy.rb +++ b/app/policies/group_member_policy.rb @@ -16,6 +16,12 @@ def rules can! :destroy_group_member end - # cannot! :update_group_member if @subject.ldap + # EE-only + can_override = Ability.allowed?(@user, :override_group_member, group) + + if can_override && @subject.ldap? + can! :override_group_member + can! :update_group_member if @subject.override? + end end end diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index 39bfb64dd11dc1..84c5446cadae1b 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -35,7 +35,10 @@ def rules end # EE-only - # cannot! :admin_group_member if @subject.ldap_synced? + if @subject.ldap_synced? + cannot! :admin_group_member + can! :override_group_member if owner + end end def can_read_group? diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index ea2a5bb578affb..58176681f0be28 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -3,9 +3,10 @@ - user = local_assigns.fetch(:user, member.user) - source = member.source - can_admin_member = can?(current_user, action_member_permission(:update, member), member) +- can_override_member = can?(current_user, action_member_permission(:override, member), member) - update_url = member.type == 'GroupMember' ? group_group_member_path(@group, member) : namespace_project_project_member_path(@project.namespace, @project, member) -%li.member{ class: [dom_class(member), ("is-overriden" if member.override && can_admin_member)], id: dom_id(member) } +%li.member{ class: [dom_class(member), ("is-overriden" if member.override)], id: dom_id(member) } %span.list-item-name - if user = image_tag avatar_icon(user, 40), class: "avatar s40", alt: '' @@ -35,7 +36,7 @@ %span{ class: ('text-warning' if member.expires_soon?) } Expires in #{distance_of_time_in_words_to_now(member.expires_at)} - - if member.ldap? + - if can_override_member %span.label.label-info.pull-right.visible-xs-block LDAP @@ -50,7 +51,7 @@ = time_ago_with_tooltip(member.created_at) - if show_roles .controls.member-controls - - if member.ldap? + - if can_override_member %span.label.label-info.members-ldap.hidden-xs LDAP - if show_controls && (member.respond_to?(:group) && @group) || (member.respond_to?(:project) && @project) @@ -73,7 +74,7 @@ = link_to role, "javascript:void(0)", class: ("is-active" if member.access_level == role_id), data: { id: role_id } - - if member.ldap? + - if can_override_member %li.divider %li = link_to "Revert to LDAP group sync settings", "javascript:void(0)", @@ -95,7 +96,7 @@ class: 'btn btn-success prepend-left-10', title: 'Grant access' - - if can?(current_user, action_member_permission(:destroy, member), member) && !member.ldap? + - if can?(current_user, action_member_permission(:destroy, member), member) - if current_user == user = link_to icon('sign-out', text: 'Leave'), polymorphic_path([:leave, member.source, :members]), method: :delete, @@ -111,7 +112,7 @@ %span.visible-xs-block Delete = icon('trash', class: 'hidden-xs') - - elsif member.ldap? && can_admin_member + - if can_override_member %button.btn.btn-default.btn-ldap-override.js-ldap-permissions{ type: "button", "aria-label" => "Edit permissions", data: { name: user.name, id: dom_id(member) } } @@ -120,7 +121,7 @@ = icon("pencil", class: "hidden-xs hidden-sm") - else %span.member-access-text= member.human_access -- if member.ldap? && can_admin_member +- if can_override_member %li.alert.alert-member-ldap{ style: "display: none;" } %p = user.name -- GitLab From a881de919c67ebcb20d00d576303fd7673126c23 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 7 Dec 2016 14:37:15 +0000 Subject: [PATCH 22/41] Removes disable on dropdowns after successful request --- app/assets/javascripts/members.js.es6 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 7024a3dfb3b293..959ab68a31a71d 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -102,11 +102,11 @@ const $btn = $(e.currentTarget); const { $memberListItem, $toggle, $dateInput } = this.getMemberListItems($btn); - this.showLDAPPermissionsWarning(e); - $toggle.removeAttr('disabled'); - $dateInput.removeAttr('disabled'); - - this.overrideLdap($memberListItem, $btn.data('endpoint'), true); + this.overrideLdap($memberListItem, $btn.data('endpoint'), true).then(() => { + this.showLDAPPermissionsWarning(e); + $toggle.removeAttr('disabled'); + $dateInput.removeAttr('disabled'); + }); } overrideLdap($memberListitem, endpoint, override) { -- GitLab From c25122b3e5a57b8480b6f8734a7a32b96d12122e Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 7 Dec 2016 15:46:55 +0000 Subject: [PATCH 23/41] Fixed some dropdown issues found when porting to CE --- app/assets/javascripts/members.js.es6 | 42 +++++++++++-------- app/views/groups/group_members/update.js.haml | 2 +- .../projects/project_members/update.js.haml | 2 +- app/views/shared/members/_member.html.haml | 10 ++--- 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 959ab68a31a71d..136247a1cf6242 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -12,8 +12,8 @@ $('.js-ldap-permissions').off('click').on('click', this.showLDAPPermissionsWarning.bind(this)); $('.js-ldap-override').off('click').on('click', this.toggleMemberAccessToggle.bind(this)); $('.project_member, .group_member').off('ajax:success').on('ajax:success', this.removeRow); - $('.js-member-update-control').off('change').on('change', this.formSubmit); - $('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess); + $('.js-member-update-control').off('change').on('change', this.formSubmit.bind(this)); + $('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess.bind(this)); gl.utils.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change'); } @@ -45,15 +45,15 @@ }, clicked: (selected, $el) => { const $link = $($el); + const { $memberListItem, $toggle, $dateInput } = this.getMemberListItems($link); - if ($link.data('revert')) { - const { $memberListItem, $toggle, $dateInput } = this.getMemberListItems($link); + $toggle.attr('disabled', true); + $dateInput.attr('disabled', true); - $toggle.attr('disabled', true); - $dateInput.attr('disabled', true); - this.overrideLdap($memberListItem, $link.data('endpoint'), false); - } else { + if (!$link.data('revert')) { $btn.closest('form').trigger('submit.rails'); + } else { + this.overrideLdap($memberListItem, $link.data('endpoint'), false); } }, }); @@ -71,13 +71,21 @@ } } - formSubmit() { - $(this).closest('form').trigger('submit.rails').end() - .disable(); + formSubmit(e) { + const $this = $(e.currentTarget); + const { $toggle, $dateInput } = this.getMemberListItems($this); + + $this.closest('form').trigger('submit.rails'); + + $toggle.attr('disabled', true); + $dateInput.attr('disabled', true); } - formSuccess() { - $(this).find('.js-member-update-control').enable(); + formSuccess(e) { + const { $toggle, $dateInput } = this.getMemberListItems($(e.currentTarget).closest('.member')); + + $toggle.removeAttr('disabled'); + $dateInput.removeAttr('disabled'); } showLDAPPermissionsWarning(e) { @@ -88,8 +96,8 @@ $ldapPermissionsElement.toggle(); } - getMemberListItems(btn) { - const $memberListItem = $(`#${btn.data('id')}`); + getMemberListItems($el) { + const $memberListItem = $el.is('.member') ? $el : $(`#${$el.data('el-id')}`); return { $memberListItem, @@ -110,8 +118,6 @@ } overrideLdap($memberListitem, endpoint, override) { - $memberListitem.toggleClass('is-overriden', override); - return $.ajax({ url: endpoint, type: 'PATCH', @@ -120,6 +126,8 @@ override, }, }, + }).then(() => { + $memberListitem.toggleClass('is-overriden', override); }); } } diff --git a/app/views/groups/group_members/update.js.haml b/app/views/groups/group_members/update.js.haml index 3e450b69561be5..9d05bff6c4e0cb 100644 --- a/app/views/groups/group_members/update.js.haml +++ b/app/views/groups/group_members/update.js.haml @@ -1,4 +1,4 @@ :plain var $listItem = $('#{escape_javascript(render('shared/members/member', member: @group_member))}'); $("##{dom_id(@group_member)} .list-item-name").replaceWith($listItem.find('.list-item-name')); - gl.utils.localTimeAgo($('.js-timeago')); + gl.utils.localTimeAgo($('.js-timeago'), $("##{dom_id(@group_member)}")); diff --git a/app/views/projects/project_members/update.js.haml b/app/views/projects/project_members/update.js.haml index ab36dd5ef18cca..d15f4310ff5ed2 100644 --- a/app/views/projects/project_members/update.js.haml +++ b/app/views/projects/project_members/update.js.haml @@ -1,4 +1,4 @@ :plain var $listItem = $('#{escape_javascript(render('shared/members/member', member: @project_member))}'); $("##{dom_id(@project_member)} .list-item-name").replaceWith($listItem.find('.list-item-name')); - gl.utils.timeAgo($('.js-timeago')); + gl.utils.localTimeAgo($('.js-timeago'), $("##{dom_id(@project_member)}")); diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index 58176681f0be28..bd8d090f7e03e2 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -73,14 +73,14 @@ %li = link_to role, "javascript:void(0)", class: ("is-active" if member.access_level == role_id), - data: { id: role_id } + data: { id: role_id, el_id: dom_id(member) } - if can_override_member %li.divider %li = link_to "Revert to LDAP group sync settings", "javascript:void(0)", - data: { revert: "true", endpoint: override_group_group_member_path(@group, member), id: dom_id(member) } + data: { revert: "true", endpoint: override_group_group_member_path(@group, member), el_id: dom_id(member) } .prepend-left-5.clearable-input.member-form-control - = f.text_field :expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: 'Expiration date', id: "member_expires_at_#{member.id}", disabled: !can_admin_member + = f.text_field :expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: 'Expiration date', id: "member_expires_at_#{member.id}", disabled: !can_admin_member, data: { el_id: dom_id(member) } %i.clear-icon.js-clear-input - else %span.member-access-text= member.human_access @@ -115,7 +115,7 @@ - if can_override_member %button.btn.btn-default.btn-ldap-override.js-ldap-permissions{ type: "button", "aria-label" => "Edit permissions", - data: { name: user.name, id: dom_id(member) } } + data: { name: user.name, el_id: dom_id(member) } } %span.visible-xs-block.visible-sm-block Edit permissions = icon("pencil", class: "hidden-xs hidden-sm") @@ -129,7 +129,7 @@ .controls %button.btn.btn-warning.js-ldap-override{ type: "button", "aria-label" => "Change LDAP member permissions", - data: { id: dom_id(member), endpoint: override_group_group_member_path(@group, member) } } + data: { el_id: dom_id(member), endpoint: override_group_group_member_path(@group, member) } } Change permissions %button.btn.btn-default.js-ldap-permissions{ type: "button", "aria-label" => "Close permissions override", -- GitLab From 7e4ff96168a6c99cf9b658d04d21e2d9439bc148 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 7 Dec 2016 15:51:59 +0000 Subject: [PATCH 24/41] Fixed LDAP warning not hiding --- app/views/shared/members/_member.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index bd8d090f7e03e2..2225da9b0d526b 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -133,5 +133,5 @@ Change permissions %button.btn.btn-default.js-ldap-permissions{ type: "button", "aria-label" => "Close permissions override", - data: { id: dom_id(member) } } + data: { el_id: dom_id(member) } } = icon("times") -- GitLab From 5ba1e26eb5284d0c532745c6c227f6ce71ec3862 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 7 Dec 2016 19:21:48 -0200 Subject: [PATCH 25/41] Allow master/owner to change non-LDAP memberships --- app/policies/group_member_policy.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/policies/group_member_policy.rb b/app/policies/group_member_policy.rb index 7b45b283bfd986..e97d318e35cdc7 100644 --- a/app/policies/group_member_policy.rb +++ b/app/policies/group_member_policy.rb @@ -19,9 +19,9 @@ def rules # EE-only can_override = Ability.allowed?(@user, :override_group_member, group) - if can_override && @subject.ldap? - can! :override_group_member - can! :update_group_member if @subject.override? + if can_override + can! :override_group_member if @subject.ldap? + can! :update_group_member if !@subject.ldap? || (@subject.ldap? && @subject.override?) end end end -- GitLab From 1afc8d83a48382867f4c7a34f0555d92840858e7 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 7 Dec 2016 19:25:04 -0200 Subject: [PATCH 26/41] Add CHANGELOG entry --- changelogs/unreleased-ee/group-sync-update-perms.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased-ee/group-sync-update-perms.yml diff --git a/changelogs/unreleased-ee/group-sync-update-perms.yml b/changelogs/unreleased-ee/group-sync-update-perms.yml new file mode 100644 index 00000000000000..5e6a11cb1f6cfe --- /dev/null +++ b/changelogs/unreleased-ee/group-sync-update-perms.yml @@ -0,0 +1,4 @@ +--- +title: Allow master/owner to change permission levels when LDAP group sync is enabled +merge_request: 822 +author: -- GitLab From 7b372007a5cacaf3ea4ce2e9ec9bbe3e9a851dde Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 7 Dec 2016 19:27:18 -0200 Subject: [PATCH 27/41] Update LDAP sync information message for linked groups --- app/views/groups/group_members/_ldap_sync.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/groups/group_members/_ldap_sync.html.haml b/app/views/groups/group_members/_ldap_sync.html.haml index 48c7318329b599..d68ffe180753cc 100644 --- a/app/views/groups/group_members/_ldap_sync.html.haml +++ b/app/views/groups/group_members/_ldap_sync.html.haml @@ -1,6 +1,6 @@ - if current_user && @group.ldap_synced? .bs-callout.bs-callout-info - The members of this group are managed using LDAP and cannot be added, changed or removed here. + The members of this group are managed using LDAP and cannot be added or removed here. Because LDAP permissions in GitLab get updated one user at a time and because GitLab caches LDAP check results, changes on your LDAP server or in this group's LDAP sync settings may take up to #{Gitlab.config.ldap['sync_time']}s to show in the list below. %ul - @group.ldap_group_links.each do |ldap_group_link| -- GitLab From 988c6d8808ce714fecf6fd2f30beb8fa3950d3fc Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 7 Dec 2016 20:18:02 -0200 Subject: [PATCH 28/41] Add test to access levels changes of users when LDAP group sync is true --- .../members/override_ldap_memberships_spec.rb | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 spec/features/groups/members/override_ldap_memberships_spec.rb diff --git a/spec/features/groups/members/override_ldap_memberships_spec.rb b/spec/features/groups/members/override_ldap_memberships_spec.rb new file mode 100644 index 00000000000000..a3d20ba4c2571d --- /dev/null +++ b/spec/features/groups/members/override_ldap_memberships_spec.rb @@ -0,0 +1,70 @@ +require 'spec_helper' + +feature 'Groups > Members > Master/Owner can override LDAP access levels', feature: true do + include WaitForAjax + + let(:johndoe) { create(:user, name: 'John Doe') } + let(:maryjane) { create(:user, name: 'Mary Jane') } + let(:owner) { create(:user) } + let(:group) { create(:group_with_ldap_group_link, :public) } + + let!(:owner_member) { create(:group_member, :owner, group: group, user: owner) } + let!(:ldap_member) { create(:group_member, :guest, group: group, user: johndoe, ldap: true) } + let!(:regular_member) { create(:group_member, :guest, group: group, user: maryjane, ldap: false) } + + background do + # We need to actually activate the LDAP config otherwise `Group#ldap_synced?` # will always be false! + allow(Gitlab.config.ldap).to receive_messages(enabled: true) + + login_as(owner) + end + + scenario 'owner can override LDAP access level', js: true do + ldap_override_message = 'John Doe is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync.' + + visit group_group_members_path(group) + + within "#group_member_#{ldap_member.id}" do + expect(page).to have_content 'LDAP' + expect(page).to have_button 'Guest', disabled: true + expect(page).to have_button 'Edit permissions' + + click_button 'Edit permissions' + end + + expect(page).to have_content ldap_override_message + + click_button 'Change permissions' + + expect(page).not_to have_content ldap_override_message + expect(page).not_to have_button 'Change permissions' + + within "#group_member_#{ldap_member.id}" do + expect(page).not_to have_button 'Edit permissions' + expect(page).to have_button 'Guest', disabled: false + + click_button 'Guest' + + within '.dropdown-menu' do + click_link 'Revert to LDAP group sync settings' + end + + wait_for_ajax + + expect(page).to have_button 'Guest', disabled: true + expect(page).to have_button 'Edit permissions' + end + + within "#group_member_#{regular_member.id}" do + expect(page).not_to have_content 'LDAP' + expect(page).not_to have_button 'Edit permissions' + expect(page).to have_button 'Guest', disabled: false + + click_button 'Guest' + + within '.dropdown-menu' do + expect(page).not_to have_content 'Revert to LDAP group sync settings' + end + end + end +end -- GitLab From e888c93570e0cf11b192139486cf20cdf8a2f319 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 8 Dec 2016 15:34:33 +0000 Subject: [PATCH 29/41] Removes weird gap when LDAP override is enabled Adds spinner to change permissions button --- app/assets/javascripts/members.js.es6 | 3 +++ app/assets/stylesheets/pages/members.scss | 6 ------ app/views/shared/members/_member.html.haml | 3 ++- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 136247a1cf6242..8d1125b774943d 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -110,10 +110,13 @@ const $btn = $(e.currentTarget); const { $memberListItem, $toggle, $dateInput } = this.getMemberListItems($btn); + $btn.attr('disabled', true).disable(); + this.overrideLdap($memberListItem, $btn.data('endpoint'), true).then(() => { this.showLDAPPermissionsWarning(e); $toggle.removeAttr('disabled'); $dateInput.removeAttr('disabled'); + $btn.removeAttr('disabled').enable(); }); } diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index 3d815e3f5e7c24..1aa3be9313230f 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -8,12 +8,6 @@ .btn-ldap-override { display: none!important; } - - .form-horizontal { - @media (min-width: $screen-sm-min) { - margin-right: 45px; - } - } } .list-item-name { diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index 2225da9b0d526b..df327aad19c19b 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -127,9 +127,10 @@ = user.name is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync. .controls - %button.btn.btn-warning.js-ldap-override{ type: "button", + %button.btn.btn-warning.btn-loading.js-ldap-override{ type: "button", "aria-label" => "Change LDAP member permissions", data: { el_id: dom_id(member), endpoint: override_group_group_member_path(@group, member) } } + = icon('spinner spin') Change permissions %button.btn.btn-default.js-ldap-permissions{ type: "button", "aria-label" => "Close permissions override", -- GitLab From cab8d0243deb92004ed950793b692c3c8277488e Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 9 Dec 2016 11:08:32 +0000 Subject: [PATCH 30/41] Fixed up JS to match CE implementation --- app/assets/javascripts/members.js.es6 | 31 +++++++++++++-------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 8d1125b774943d..f0200d4f1f2079 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -43,16 +43,15 @@ return $el.text(); }, - clicked: (selected, $el) => { - const $link = $($el); + clicked: (selected, $link) => { const { $memberListItem, $toggle, $dateInput } = this.getMemberListItems($link); - $toggle.attr('disabled', true); - $dateInput.attr('disabled', true); - if (!$link.data('revert')) { - $btn.closest('form').trigger('submit.rails'); + this.formSubmit(null, $link); } else { + $toggle.disable(); + $dateInput.disable(); + this.overrideLdap($memberListItem, $link.data('endpoint'), false); } }, @@ -71,21 +70,21 @@ } } - formSubmit(e) { - const $this = $(e.currentTarget); + formSubmit(e, $el = null) { + const $this = e? $(e.currentTarget) : $el; const { $toggle, $dateInput } = this.getMemberListItems($this); $this.closest('form').trigger('submit.rails'); - $toggle.attr('disabled', true); - $dateInput.attr('disabled', true); + $toggle.disable(); + $dateInput.disable(); } formSuccess(e) { const { $toggle, $dateInput } = this.getMemberListItems($(e.currentTarget).closest('.member')); - $toggle.removeAttr('disabled'); - $dateInput.removeAttr('disabled'); + $toggle.enable(); + $dateInput.enable(); } showLDAPPermissionsWarning(e) { @@ -110,13 +109,13 @@ const $btn = $(e.currentTarget); const { $memberListItem, $toggle, $dateInput } = this.getMemberListItems($btn); - $btn.attr('disabled', true).disable(); + $btn.disable(); this.overrideLdap($memberListItem, $btn.data('endpoint'), true).then(() => { this.showLDAPPermissionsWarning(e); - $toggle.removeAttr('disabled'); - $dateInput.removeAttr('disabled'); - $btn.removeAttr('disabled').enable(); + $toggle.enable(); + $dateInput.enable(); + $btn.enable(); }); } -- GitLab From 9d86301388c07a53c78e1efc2894ea7351e271aa Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 9 Dec 2016 15:33:15 +0000 Subject: [PATCH 31/41] Fixed JS eslint failure --- app/assets/javascripts/members.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index f0200d4f1f2079..5f2d63898c9a76 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -71,7 +71,7 @@ } formSubmit(e, $el = null) { - const $this = e? $(e.currentTarget) : $el; + const $this = e ? $(e.currentTarget) : $el; const { $toggle, $dateInput } = this.getMemberListItems($this); $this.closest('form').trigger('submit.rails'); -- GitLab From 9198ce3a548f2479602f1bd4803353187934ceab Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 9 Dec 2016 16:30:21 +0000 Subject: [PATCH 32/41] Fixed SCSS lint --- app/assets/stylesheets/framework/variables.scss | 1 + app/assets/stylesheets/pages/members.scss | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index e94f0e09f2f868..f4778944b90166 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -197,6 +197,7 @@ $divergence-graph-bar-bg: #ccc; $divergence-graph-separator-bg: #ccc; $issue-box-upcoming-bg: #8f8f8f; $pages-group-name-color: #4c4e54; +$ldap-members-override-bg: #fff1e0; /* * Common component specific colors diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index 1aa3be9313230f..dc88cdca77c0a5 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -116,7 +116,7 @@ } .alert-member-ldap { - background-color: #fff1e0; + background-color: $ldap-members-override-bg; @media (min-width: $screen-sm-min) { line-height: 40px; -- GitLab From b320647f7013e8a5c1d93afec50b898f57747588 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 12 Dec 2016 10:51:52 +0000 Subject: [PATCH 33/41] Changed variable names in members.js.es6 --- app/assets/javascripts/members.js.es6 | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 5f2d63898c9a76..fdcd84874b7d53 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -24,13 +24,11 @@ $btn.glDropdown({ selectable: true, isSelectable(selected, $el) { - const $link = $($el); - - if ($link.data('revert')) { + if ($el.data('revert')) { return false; } - return !$link.hasClass('is-active'); + return !$el.hasClass('is-active'); }, fieldName: $btn.data('field-name'), id(selected, $el) { @@ -44,11 +42,11 @@ return $el.text(); }, clicked: (selected, $link) => { - const { $memberListItem, $toggle, $dateInput } = this.getMemberListItems($link); - if (!$link.data('revert')) { this.formSubmit(null, $link); } else { + const { $memberListItem, $toggle, $dateInput } = this.getMemberListItems($link); + $toggle.disable(); $dateInput.disable(); -- GitLab From feff94d43caad64a92464812055e780f7879b565 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 12 Dec 2016 11:34:44 -0200 Subject: [PATCH 34/41] Remove extra `#` from comment on override LDAP memberships feature spec --- spec/features/groups/members/override_ldap_memberships_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/groups/members/override_ldap_memberships_spec.rb b/spec/features/groups/members/override_ldap_memberships_spec.rb index a3d20ba4c2571d..2401462c11bcf0 100644 --- a/spec/features/groups/members/override_ldap_memberships_spec.rb +++ b/spec/features/groups/members/override_ldap_memberships_spec.rb @@ -13,7 +13,7 @@ let!(:regular_member) { create(:group_member, :guest, group: group, user: maryjane, ldap: false) } background do - # We need to actually activate the LDAP config otherwise `Group#ldap_synced?` # will always be false! + # We need to actually activate the LDAP config otherwise `Group#ldap_synced?` will always be false! allow(Gitlab.config.ldap).to receive_messages(enabled: true) login_as(owner) -- GitLab From 6f5957226945c977b61219296f8771b36aa152c9 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 12 Dec 2016 11:36:41 -0200 Subject: [PATCH 35/41] Use unless over if for negative condition on the GroupMemberPolicy --- app/policies/group_member_policy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/policies/group_member_policy.rb b/app/policies/group_member_policy.rb index e97d318e35cdc7..65041c29e25e31 100644 --- a/app/policies/group_member_policy.rb +++ b/app/policies/group_member_policy.rb @@ -21,7 +21,7 @@ def rules if can_override can! :override_group_member if @subject.ldap? - can! :update_group_member if !@subject.ldap? || (@subject.ldap? && @subject.override?) + can! :update_group_member unless @subject.ldap? && !@subject.override? end end end -- GitLab From ea0a14e85d8927f61116fd4d089a6525d8150591 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 12 Dec 2016 12:25:45 -0200 Subject: [PATCH 36/41] Move override LDAP members permissions to partials --- app/views/shared/members/_member.html.haml | 42 ++++--------------- .../shared/members/ee/_ldap_tag.html.haml | 10 +++++ .../ee/_override_member_buttons.html.haml | 29 +++++++++++++ .../_revert_ldap_group_sync_option.html.haml | 9 ++++ 4 files changed, 56 insertions(+), 34 deletions(-) create mode 100644 app/views/shared/members/ee/_ldap_tag.html.haml create mode 100644 app/views/shared/members/ee/_override_member_buttons.html.haml create mode 100644 app/views/shared/members/ee/_revert_ldap_group_sync_option.html.haml diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index df327aad19c19b..1cf911f2ccf539 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -3,8 +3,9 @@ - user = local_assigns.fetch(:user, member.user) - source = member.source - can_admin_member = can?(current_user, action_member_permission(:update, member), member) + +-# EE-only - can_override_member = can?(current_user, action_member_permission(:override, member), member) -- update_url = member.type == 'GroupMember' ? group_group_member_path(@group, member) : namespace_project_project_member_path(@project.namespace, @project, member) %li.member{ class: [dom_class(member), ("is-overriden" if member.override)], id: dom_id(member) } %span.list-item-name @@ -36,9 +37,7 @@ %span{ class: ('text-warning' if member.expires_soon?) } Expires in #{distance_of_time_in_words_to_now(member.expires_at)} - - if can_override_member - %span.label.label-info.pull-right.visible-xs-block - LDAP + = render 'shared/members/ee/ldap_tag', can_override: can_override_member, visible: true - else = image_tag avatar_icon(member.invite_email, 40), class: "avatar s40", alt: '' @@ -51,9 +50,7 @@ = time_ago_with_tooltip(member.created_at) - if show_roles .controls.member-controls - - if can_override_member - %span.label.label-info.members-ldap.hidden-xs - LDAP + = render 'shared/members/ee/ldap_tag', can_override: can_override_member, visible: false - if show_controls && (member.respond_to?(:group) && @group) || (member.respond_to?(:project) && @project) - if user != current_user = form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f| @@ -74,11 +71,7 @@ = link_to role, "javascript:void(0)", class: ("is-active" if member.access_level == role_id), data: { id: role_id, el_id: dom_id(member) } - - if can_override_member - %li.divider - %li - = link_to "Revert to LDAP group sync settings", "javascript:void(0)", - data: { revert: "true", endpoint: override_group_group_member_path(@group, member), el_id: dom_id(member) } + = render 'shared/members/ee/revert_ldap_group_sync_option', group: @group, member: member, can_override: can_override_member .prepend-left-5.clearable-input.member-form-control = f.text_field :expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: 'Expiration date', id: "member_expires_at_#{member.id}", disabled: !can_admin_member, data: { el_id: dom_id(member) } %i.clear-icon.js-clear-input @@ -112,27 +105,8 @@ %span.visible-xs-block Delete = icon('trash', class: 'hidden-xs') - - if can_override_member - %button.btn.btn-default.btn-ldap-override.js-ldap-permissions{ type: "button", - "aria-label" => "Edit permissions", - data: { name: user.name, el_id: dom_id(member) } } - %span.visible-xs-block.visible-sm-block - Edit permissions - = icon("pencil", class: "hidden-xs hidden-sm") + = render 'shared/members/ee/override_member_buttons', group: @group, member: member, user: user, action: :edit, can_override: can_override_member - else %span.member-access-text= member.human_access -- if can_override_member - %li.alert.alert-member-ldap{ style: "display: none;" } - %p - = user.name - is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync. - .controls - %button.btn.btn-warning.btn-loading.js-ldap-override{ type: "button", - "aria-label" => "Change LDAP member permissions", - data: { el_id: dom_id(member), endpoint: override_group_group_member_path(@group, member) } } - = icon('spinner spin') - Change permissions - %button.btn.btn-default.js-ldap-permissions{ type: "button", - "aria-label" => "Close permissions override", - data: { el_id: dom_id(member) } } - = icon("times") + += render 'shared/members/ee/override_member_buttons', group: @group, member: member, user: user, action: :confirm, can_override: can_override_member diff --git a/app/views/shared/members/ee/_ldap_tag.html.haml b/app/views/shared/members/ee/_ldap_tag.html.haml new file mode 100644 index 00000000000000..468c524670ed8b --- /dev/null +++ b/app/views/shared/members/ee/_ldap_tag.html.haml @@ -0,0 +1,10 @@ +- can_override = local_assigns.fetch(:can_override, false) +- visible = local_assigns.fetch(:visible, false) + +- if can_override + - if visible + %span.label.label-info.pull-right.visible-xs-block + LDAP + - else + %span.label.label-info.members-ldap.hidden-xs + LDAP diff --git a/app/views/shared/members/ee/_override_member_buttons.html.haml b/app/views/shared/members/ee/_override_member_buttons.html.haml new file mode 100644 index 00000000000000..80e8dc6d104c37 --- /dev/null +++ b/app/views/shared/members/ee/_override_member_buttons.html.haml @@ -0,0 +1,29 @@ +- group = local_assigns.fetch(:group) +- member = local_assigns.fetch(:member) +- user = local_assigns.fetch(:user) +- action = local_assigns.fetch(:action, :edit).to_s.inquiry +- can_override = local_assigns.fetch(:can_override, false) + +- if can_override + - if action.edit? + %button.btn.btn-default.btn-ldap-override.js-ldap-permissions{ type: 'button', + 'aria-label' => 'Edit permissions', + data: { name: user.name, el_id: dom_id(member) } } + %span.visible-xs-block.visible-sm-block + Edit permissions + = icon('pencil', class: 'hidden-xs hidden-sm') + - else + %li.alert.alert-member-ldap{ style: 'display: none;' } + %p + = user.name + is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync. + .controls + %button.btn.btn-warning.btn-loading.js-ldap-override{ type: 'button', + 'aria-label' => 'Change LDAP member permissions', + data: { el_id: dom_id(member), endpoint: override_group_group_member_path(group, member) } } + = icon('spinner spin') + Change permissions + %button.btn.btn-default.js-ldap-permissions{ type: 'button', + 'aria-label' => 'Close permissions override', + data: { el_id: dom_id(member) } } + = icon('times') diff --git a/app/views/shared/members/ee/_revert_ldap_group_sync_option.html.haml b/app/views/shared/members/ee/_revert_ldap_group_sync_option.html.haml new file mode 100644 index 00000000000000..5a431a3ec2f56a --- /dev/null +++ b/app/views/shared/members/ee/_revert_ldap_group_sync_option.html.haml @@ -0,0 +1,9 @@ +- group = local_assigns.fetch(:group) +- member = local_assigns.fetch(:member) +- can_override = local_assigns.fetch(:can_override, false) + +- if can_override + %li.divider + %li + = link_to 'Revert to LDAP group sync settings', 'javascript:void(0)', + data: { revert: 'true', endpoint: override_group_group_member_path(group, member), el_id: dom_id(member) } -- GitLab From c6dab662d3c85be0e31c4b75ccb4a38c1fa0a58e Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 12 Dec 2016 12:32:14 -0200 Subject: [PATCH 37/41] Remove unnecessary return on authorize_update_group_member! --- app/controllers/groups/group_members_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 698bc283b783fb..474a0401e3a9b6 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -97,7 +97,7 @@ def override def authorize_update_group_member! unless can?(current_user, :admin_group_member, group) || can?(current_user, :override_group_member, group) - return render_403 + render_403 end end -- GitLab From 547d5f8f1fd39779cbcd113473168c6d61c34faa Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 12 Dec 2016 13:17:04 -0200 Subject: [PATCH 38/41] Move EE code on Groups::GroupMembersController to EE namespace --- .../ee/groups/group_members_controller.rb | 31 +++++++++++++++++++ .../groups/group_members_controller.rb | 20 ++---------- 2 files changed, 33 insertions(+), 18 deletions(-) create mode 100644 app/controllers/ee/groups/group_members_controller.rb diff --git a/app/controllers/ee/groups/group_members_controller.rb b/app/controllers/ee/groups/group_members_controller.rb new file mode 100644 index 00000000000000..ac678ce1ad9206 --- /dev/null +++ b/app/controllers/ee/groups/group_members_controller.rb @@ -0,0 +1,31 @@ +module EE + module Groups + module GroupMembersController + def override + @group_member = @group.group_members.find(params[:id]) + + return render_403 unless can?(current_user, :override_group_member, @group_member) + + if @group_member.update_attributes(override_params) + log_audit_event(@group_member, action: :override) + + respond_to do |format| + format.js { head :ok } + end + end + end + + protected + + def authorize_update_group_member! + unless can?(current_user, :admin_group_member, group) || can?(current_user, :override_group_member, group) + render_403 + end + end + + def override_params + params.require(:group_member).permit(:override) + end + end + end +end diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 474a0401e3a9b6..213ca5366b708e 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -1,4 +1,6 @@ class Groups::GroupMembersController < Groups::ApplicationController + prepend EE::Groups::GroupMembersController + include MembershipActions # Authorize @@ -79,20 +81,6 @@ def resend_invite end end - def override - @group_member = @group.group_members.find(params[:id]) - - return render_403 unless can?(current_user, :override_group_member, @group_member) - - if @group_member.update_attributes(override_params) - log_audit_event(@group_member, action: :override) - - respond_to do |format| - format.js { head :ok } - end - end - end - protected def authorize_update_group_member! @@ -105,10 +93,6 @@ def member_params params.require(:group_member).permit(:access_level, :user_id, :expires_at) end - def override_params - params.require(:group_member).permit(:override) - end - # MembershipActions concern alias_method :membershipable, :group end -- GitLab From 657da7b4a56ed6887f7316361db20ab38d0afe57 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 12 Dec 2016 13:22:20 -0200 Subject: [PATCH 39/41] Remove duplicated method on Groups::GroupMembersController --- app/controllers/groups/group_members_controller.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 213ca5366b708e..379a7e0edabb2f 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -83,12 +83,6 @@ def resend_invite protected - def authorize_update_group_member! - unless can?(current_user, :admin_group_member, group) || can?(current_user, :override_group_member, group) - render_403 - end - end - def member_params params.require(:group_member).permit(:access_level, :user_id, :expires_at) end -- GitLab From 68d90b966da434fe0b8d3569c72f68545e753caa Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 13 Dec 2016 11:36:27 -0200 Subject: [PATCH 40/41] Extract hook methods from group policies with EE-specific implementation --- app/policies/group_member_policy.rb | 7 +++++-- app/policies/group_policy.rb | 13 ++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/policies/group_member_policy.rb b/app/policies/group_member_policy.rb index 65041c29e25e31..8709f1d21cad62 100644 --- a/app/policies/group_member_policy.rb +++ b/app/policies/group_member_policy.rb @@ -16,8 +16,11 @@ def rules can! :destroy_group_member end - # EE-only - can_override = Ability.allowed?(@user, :override_group_member, group) + additional_rules! + end + + def additional_rules! + can_override = Ability.allowed?(@user, :override_group_member, @subject.group) if can_override can! :override_group_member if @subject.ldap? diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index 84c5446cadae1b..0180e10eae241f 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -34,11 +34,7 @@ def rules can! :request_access end - # EE-only - if @subject.ldap_synced? - cannot! :admin_group_member - can! :override_group_member if owner - end + additional_rules!(master) end def can_read_group? @@ -49,4 +45,11 @@ def can_read_group? GroupProjectsFinder.new(@subject).execute(@user).any? end + + def additional_rules!(master) + if @subject.ldap_synced? + cannot! :admin_group_member + can! :override_group_member if master + end + end end -- GitLab From 04c053e0a49424acffb2d8a0a29d81cbbe7d91e7 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 16 Dec 2016 09:27:29 +0000 Subject: [PATCH 41/41] Added error handling --- app/assets/javascripts/members.js.es6 | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index fdcd84874b7d53..b097eb765f32e1 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -1,4 +1,6 @@ /* eslint-disable class-methods-use-this */ +/* eslint-disable no-new */ +/* global Flash */ (() => { window.gl = window.gl || {}; @@ -50,7 +52,10 @@ $toggle.disable(); $dateInput.disable(); - this.overrideLdap($memberListItem, $link.data('endpoint'), false); + this.overrideLdap($memberListItem, $link.data('endpoint'), false).fail(() => { + $toggle.enable(); + $dateInput.enable(); + }); } }, }); @@ -111,9 +116,17 @@ this.overrideLdap($memberListItem, $btn.data('endpoint'), true).then(() => { this.showLDAPPermissionsWarning(e); + $toggle.enable(); $dateInput.enable(); + }).fail((xhr) => { $btn.enable(); + + if (xhr.status === 403) { + new Flash('You do not have the correct permissions to override the settings from the LDAP group sync.', 'alert'); + } else { + new Flash('An error occured whilst saving LDAP override status. Please try again.', 'alert'); + } }); } -- GitLab