From f059f933dad0895ac5cfa42f8f513a8014cd7c42 Mon Sep 17 00:00:00 2001
From: Peter Hegman <phegman@gitlab.com>
Date: Mon, 1 Apr 2024 16:08:07 -0700
Subject: [PATCH] Check `allow_organization_creation` feature flag on a few
 more views

Check in admin area and empty state. Also 404 new organization page
if disabled.
---
 .../organizations/index/components/app.vue    |  5 +++-
 .../shared/components/organizations_view.vue  | 28 +++++++++++++------
 .../admin/organizations_controller.rb         |  3 ++
 .../organizations/application_controller.rb   |  1 +
 .../index/components/app_spec.js              | 15 ++++++++++
 .../components/organizations_view_spec.js     | 16 +++++++++++
 .../organizations_controller_spec.rb          | 11 ++++++++
 7 files changed, 69 insertions(+), 10 deletions(-)

diff --git a/app/assets/javascripts/admin/organizations/index/components/app.vue b/app/assets/javascripts/admin/organizations/index/components/app.vue
index c2a3f016f71ac..de97fa478db6f 100644
--- a/app/assets/javascripts/admin/organizations/index/components/app.vue
+++ b/app/assets/javascripts/admin/organizations/index/components/app.vue
@@ -46,6 +46,9 @@ export default {
     showHeader() {
       return this.loading || this.organizations.nodes?.length;
     },
+    showNewOrganizationButton() {
+      return gon.features?.allowOrganizationCreation;
+    },
     loading() {
       return this.$apollo.queries.organizations.loading;
     },
@@ -78,7 +81,7 @@ export default {
       class="gl-display-flex gl-align-items-center gl-justify-content-space-between gl-mb-5"
     >
       <h1 class="gl-m-0 gl-font-size-h-display">{{ $options.i18n.pageTitle }}</h1>
-      <gl-button :href="newOrganizationUrl" variant="confirm">{{
+      <gl-button v-if="showNewOrganizationButton" :href="newOrganizationUrl" variant="confirm">{{
         $options.i18n.newOrganization
       }}</gl-button>
     </div>
diff --git a/app/assets/javascripts/organizations/shared/components/organizations_view.vue b/app/assets/javascripts/organizations/shared/components/organizations_view.vue
index 4fa17d4cffd03..62434de9c88f7 100644
--- a/app/assets/javascripts/organizations/shared/components/organizations_view.vue
+++ b/app/assets/javascripts/organizations/shared/components/organizations_view.vue
@@ -34,6 +34,24 @@ export default {
     nodes() {
       return this.organizations.nodes || [];
     },
+    emptyStateProps() {
+      const baseProps = {
+        svgHeight: 144,
+        svgPath: this.organizationsEmptyStateSvgPath,
+        title: this.$options.i18n.emptyStateTitle,
+        description: this.$options.i18n.emptyStateDescription,
+      };
+
+      if (gon.features?.allowOrganizationCreation) {
+        return {
+          ...baseProps,
+          primaryButtonLink: this.newOrganizationUrl,
+          primaryButtonText: this.$options.i18n.emptyStateButtonText,
+        };
+      }
+
+      return baseProps;
+    },
   },
 };
 </script>
@@ -47,13 +65,5 @@ export default {
     @prev="$emit('prev', $event)"
     @next="$emit('next', $event)"
   />
-  <gl-empty-state
-    v-else
-    :svg-height="144"
-    :svg-path="organizationsEmptyStateSvgPath"
-    :title="$options.i18n.emptyStateTitle"
-    :description="$options.i18n.emptyStateDescription"
-    :primary-button-link="newOrganizationUrl"
-    :primary-button-text="$options.i18n.emptyStateButtonText"
-  />
+  <gl-empty-state v-else v-bind="emptyStateProps" />
 </template>
diff --git a/app/controllers/admin/organizations_controller.rb b/app/controllers/admin/organizations_controller.rb
index 55895f8bfac26..dc48fe7507ed0 100644
--- a/app/controllers/admin/organizations_controller.rb
+++ b/app/controllers/admin/organizations_controller.rb
@@ -5,6 +5,9 @@ class OrganizationsController < ApplicationController
     feature_category :cell
 
     before_action :check_feature_flag!
+    before_action only: [:index] do
+      push_frontend_feature_flag(:allow_organization_creation, current_user)
+    end
 
     def index; end
 
diff --git a/app/controllers/organizations/application_controller.rb b/app/controllers/organizations/application_controller.rb
index b4a050835c0d5..180b114370b7f 100644
--- a/app/controllers/organizations/application_controller.rb
+++ b/app/controllers/organizations/application_controller.rb
@@ -21,6 +21,7 @@ def check_feature_flag!
     end
 
     def authorize_create_organization!
+      access_denied! unless Feature.enabled?(:allow_organization_creation, current_user)
       access_denied! unless can?(current_user, :create_organization)
     end
 
diff --git a/spec/frontend/admin/organizations/index/components/app_spec.js b/spec/frontend/admin/organizations/index/components/app_spec.js
index 00eda36608cd3..72339303e25f3 100644
--- a/spec/frontend/admin/organizations/index/components/app_spec.js
+++ b/spec/frontend/admin/organizations/index/components/app_spec.js
@@ -47,6 +47,10 @@ describe('AdminOrganizationsIndexApp', () => {
     });
   };
 
+  beforeEach(() => {
+    gon.features = { allowOrganizationCreation: true };
+  });
+
   afterEach(() => {
     mockApollo = null;
   });
@@ -119,6 +123,17 @@ describe('AdminOrganizationsIndexApp', () => {
     });
   });
 
+  describe('when `allowOrganizationCreation` feature flag is disabled', () => {
+    beforeEach(() => {
+      gon.features = { allowOrganizationCreation: false };
+
+      createComponent();
+      return waitForPromises();
+    });
+
+    itDoesNotRenderNewOrganizationButton();
+  });
+
   describe('when API call is successful and returns no organizations', () => {
     beforeEach(async () => {
       createComponent(
diff --git a/spec/frontend/organizations/shared/components/organizations_view_spec.js b/spec/frontend/organizations/shared/components/organizations_view_spec.js
index 7592ca30493a7..270998eba1f79 100644
--- a/spec/frontend/organizations/shared/components/organizations_view_spec.js
+++ b/spec/frontend/organizations/shared/components/organizations_view_spec.js
@@ -24,6 +24,10 @@ describe('OrganizationsView', () => {
   const findOrganizationsList = () => wrapper.findComponent(OrganizationsList);
   const findGlEmptyState = () => wrapper.findComponent(GlEmptyState);
 
+  beforeEach(() => {
+    gon.features = { allowOrganizationCreation: true };
+  });
+
   describe.each`
     description                                    | loading  | orgsData         | emptyStateSvg               | emptyStateUrl
     ${'when loading'}                              | ${true}  | ${[]}            | ${false}                    | ${false}
@@ -55,6 +59,18 @@ describe('OrganizationsView', () => {
     });
   });
 
+  describe('when `allowOrganizationCreation` feature flag is disabled', () => {
+    beforeEach(() => {
+      gon.features = { allowOrganizationCreation: false };
+      createComponent({ loading: false, organizations: { nodes: [], pageInfo: {} } });
+    });
+
+    it('does not render `New organization` button in empty state', () => {
+      expect(findGlEmptyState().attributes('primarybuttonlink')).toBeUndefined();
+      expect(findGlEmptyState().attributes('primarybuttontext')).toBeUndefined();
+    });
+  });
+
   describe('when `OrganizationsList` emits `next` event', () => {
     const endCursor = 'mockEndCursor';
 
diff --git a/spec/requests/organizations/organizations_controller_spec.rb b/spec/requests/organizations/organizations_controller_spec.rb
index ba8edfcf05e76..114c2e6b66076 100644
--- a/spec/requests/organizations/organizations_controller_spec.rb
+++ b/spec/requests/organizations/organizations_controller_spec.rb
@@ -153,6 +153,17 @@
     subject(:gitlab_request) { get new_organization_path }
 
     it_behaves_like 'controller action that requires authentication by any user'
+
+    context 'when user is signed in and `allow_organization_creation` feature flag is disabled' do
+      let_it_be(:user) { create(:user) }
+
+      before do
+        stub_feature_flags(allow_organization_creation: false)
+        sign_in(user)
+      end
+
+      it_behaves_like 'organization - not found response'
+    end
   end
 
   describe 'GET #index' do
-- 
GitLab