Commit b733c4c0 authored by Reuben Pereira's avatar Reuben Pereira

Allow setting of container registry visibility in project settings UI

Add a select dropdown under
`Settings > General > Visibility, project features, permissions` that
allows project admins to change the visibility of the container
registry.

The container registry visibility can now be set independently of the
project visibility.

Changelog: changed
parent 04550162
...@@ -11,6 +11,7 @@ import { ...@@ -11,6 +11,7 @@ import {
featureAccessLevel, featureAccessLevel,
featureAccessLevelNone, featureAccessLevelNone,
CVE_ID_REQUEST_BUTTON_I18N, CVE_ID_REQUEST_BUTTON_I18N,
featureAccessLevelDescriptions,
} from '../constants'; } from '../constants';
import { toggleHiddenClassBySelector } from '../external'; import { toggleHiddenClassBySelector } from '../external';
import projectFeatureSetting from './project_feature_setting.vue'; import projectFeatureSetting from './project_feature_setting.vue';
...@@ -176,7 +177,7 @@ export default { ...@@ -176,7 +177,7 @@ export default {
requirementsAccessLevel: featureAccessLevel.EVERYONE, requirementsAccessLevel: featureAccessLevel.EVERYONE,
securityAndComplianceAccessLevel: featureAccessLevel.PROJECT_MEMBERS, securityAndComplianceAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
operationsAccessLevel: featureAccessLevel.EVERYONE, operationsAccessLevel: featureAccessLevel.EVERYONE,
containerRegistryEnabled: true, containerRegistryAccessLevel: featureAccessLevel.EVERYONE,
lfsEnabled: true, lfsEnabled: true,
requestAccessEnabled: true, requestAccessEnabled: true,
highlightChangesClass: false, highlightChangesClass: false,
...@@ -184,6 +185,8 @@ export default { ...@@ -184,6 +185,8 @@ export default {
cveIdRequestEnabled: true, cveIdRequestEnabled: true,
featureAccessLevelEveryone, featureAccessLevelEveryone,
featureAccessLevelMembers, featureAccessLevelMembers,
featureAccessLevel,
featureAccessLevelDescriptions,
}; };
return { ...defaults, ...this.currentSettings }; return { ...defaults, ...this.currentSettings };
...@@ -248,7 +251,10 @@ export default { ...@@ -248,7 +251,10 @@ export default {
}, },
showContainerRegistryPublicNote() { showContainerRegistryPublicNote() {
return this.visibilityLevel === visibilityOptions.PUBLIC; return (
this.visibilityLevel === visibilityOptions.PUBLIC &&
this.containerRegistryAccessLevel === featureAccessLevel.EVERYONE
);
}, },
repositoryHelpText() { repositoryHelpText() {
...@@ -310,6 +316,10 @@ export default { ...@@ -310,6 +316,10 @@ export default {
featureAccessLevel.PROJECT_MEMBERS, featureAccessLevel.PROJECT_MEMBERS,
this.operationsAccessLevel, this.operationsAccessLevel,
); );
this.containerRegistryAccessLevel = Math.min(
featureAccessLevel.PROJECT_MEMBERS,
this.containerRegistryAccessLevel,
);
if (this.pagesAccessLevel === featureAccessLevel.EVERYONE) { if (this.pagesAccessLevel === featureAccessLevel.EVERYONE) {
// When from Internal->Private narrow access for only members // When from Internal->Private narrow access for only members
this.pagesAccessLevel = featureAccessLevel.PROJECT_MEMBERS; this.pagesAccessLevel = featureAccessLevel.PROJECT_MEMBERS;
...@@ -339,6 +349,8 @@ export default { ...@@ -339,6 +349,8 @@ export default {
this.requirementsAccessLevel = featureAccessLevel.EVERYONE; this.requirementsAccessLevel = featureAccessLevel.EVERYONE;
if (this.operationsAccessLevel === featureAccessLevel.PROJECT_MEMBERS) if (this.operationsAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
this.operationsAccessLevel = featureAccessLevel.EVERYONE; this.operationsAccessLevel = featureAccessLevel.EVERYONE;
if (this.containerRegistryAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
this.containerRegistryAccessLevel = featureAccessLevel.EVERYONE;
this.highlightChanges(); this.highlightChanges();
} }
...@@ -521,19 +533,24 @@ export default { ...@@ -521,19 +533,24 @@ export default {
" "
> >
<div v-if="showContainerRegistryPublicNote" class="text-muted"> <div v-if="showContainerRegistryPublicNote" class="text-muted">
{{ <gl-sprintf
s__( :message="
'ProjectSettings|Note: the container registry is always visible when a project is public', s__(
) `ProjectSettings|Note: The container registry is always visible when a project is public and the container registry is set to '%{access_level_description}'`,
}} )
"
>
<template #access_level_description>{{
featureAccessLevelDescriptions[featureAccessLevel.EVERYONE]
}}</template>
</gl-sprintf>
</div> </div>
<gl-toggle <project-feature-setting
v-model="containerRegistryEnabled" v-model="containerRegistryAccessLevel"
class="gl-my-2" :options="repoFeatureAccessLevelOptions"
:disabled="!repositoryEnabled" :disabled-input="!repositoryEnabled"
:label="$options.i18n.containerRegistryLabel" :label="$options.i18n.containerRegistryLabel"
label-position="hidden" name="project[project_feature_attributes][container_registry_access_level]"
name="project[container_registry_enabled]"
/> />
</project-setting-row> </project-setting-row>
<project-setting-row <project-setting-row
......
...@@ -22,7 +22,7 @@ export const featureAccessLevel = { ...@@ -22,7 +22,7 @@ export const featureAccessLevel = {
EVERYONE: 20, EVERYONE: 20,
}; };
const featureAccessLevelDescriptions = { export const featureAccessLevelDescriptions = {
[featureAccessLevel.NOT_ENABLED]: __('Enable feature to choose access level'), [featureAccessLevel.NOT_ENABLED]: __('Enable feature to choose access level'),
[featureAccessLevel.PROJECT_MEMBERS]: __('Only Project Members'), [featureAccessLevel.PROJECT_MEMBERS]: __('Only Project Members'),
[featureAccessLevel.EVERYONE]: __('Everyone With Access'), [featureAccessLevel.EVERYONE]: __('Everyone With Access'),
......
...@@ -745,6 +745,25 @@ You can, however, remove the Container Registry for a project: ...@@ -745,6 +745,25 @@ You can, however, remove the Container Registry for a project:
The **Packages & Registries > Container Registry** entry is removed from the project's sidebar. The **Packages & Registries > Container Registry** entry is removed from the project's sidebar.
## Set visibility of the Container Registry
By default, the Container Registry is visible to everyone with access to the project.
You can, however, change the visibility of the Container Registry for a project:
1. Go to your project's **Settings > General** page.
1. Expand the section **Visibility, project features, permissions**.
1. Under **Container Registry**, select an option from the dropdown:
- **Everyone With Access** (Default): The Container Registry is visible to everyone with access
to the project. If the project is public, the Container Registry is also public. If the project
is internal or private, the Container Registry is also internal or private.
- **Only Project Members**: The Container Registry is visible only to project members with
Reporter role or higher. This is similar to the behavior of a private project with Container
Registry visibility set to **Everyone With Access**.
1. Select **Save changes**.
## Manifest lists and garbage collection ## Manifest lists and garbage collection
Manifest lists are commonly used for creating multi-architecture images. If you rely on manifest Manifest lists are commonly used for creating multi-architecture images. If you rely on manifest
......
...@@ -25956,7 +25956,7 @@ msgstr "" ...@@ -25956,7 +25956,7 @@ msgstr ""
msgid "ProjectSettings|No merge commits are created." msgid "ProjectSettings|No merge commits are created."
msgstr "" msgstr ""
msgid "ProjectSettings|Note: the container registry is always visible when a project is public" msgid "ProjectSettings|Note: The container registry is always visible when a project is public and the container registry is set to '%{access_level_description}'"
msgstr "" msgstr ""
msgid "ProjectSettings|Only signed commits can be pushed to this repository." msgid "ProjectSettings|Only signed commits can be pushed to this repository."
......
import { GlToggle } from '@gitlab/ui'; import { GlSprintf, GlToggle } from '@gitlab/ui';
import { shallowMount, mount } from '@vue/test-utils'; import { shallowMount, mount } from '@vue/test-utils';
import projectFeatureSetting from '~/pages/projects/shared/permissions/components/project_feature_setting.vue'; import projectFeatureSetting from '~/pages/projects/shared/permissions/components/project_feature_setting.vue';
import settingsPanel from '~/pages/projects/shared/permissions/components/settings_panel.vue'; import settingsPanel from '~/pages/projects/shared/permissions/components/settings_panel.vue';
...@@ -22,7 +22,7 @@ const defaultProps = { ...@@ -22,7 +22,7 @@ const defaultProps = {
operationsAccessLevel: 20, operationsAccessLevel: 20,
pagesAccessLevel: 10, pagesAccessLevel: 10,
analyticsAccessLevel: 20, analyticsAccessLevel: 20,
containerRegistryEnabled: true, containerRegistryAccessLevel: 20,
lfsEnabled: true, lfsEnabled: true,
emailsDisabled: false, emailsDisabled: false,
packagesEnabled: true, packagesEnabled: true,
...@@ -85,8 +85,10 @@ describe('Settings Panel', () => { ...@@ -85,8 +85,10 @@ describe('Settings Panel', () => {
const findBuildsAccessLevelInput = () => const findBuildsAccessLevelInput = () =>
wrapper.find('[name="project[project_feature_attributes][builds_access_level]"]'); wrapper.find('[name="project[project_feature_attributes][builds_access_level]"]');
const findContainerRegistrySettings = () => wrapper.find({ ref: 'container-registry-settings' }); const findContainerRegistrySettings = () => wrapper.find({ ref: 'container-registry-settings' });
const findContainerRegistryEnabledInput = () => const findContainerRegistryPublicNoteGlSprintfComponent = () =>
wrapper.find('[name="project[container_registry_enabled]"]'); findContainerRegistrySettings().findComponent(GlSprintf);
const findContainerRegistryAccessLevelInput = () =>
wrapper.find('[name="project[project_feature_attributes][container_registry_access_level]"]');
const findPackageSettings = () => wrapper.find({ ref: 'package-settings' }); const findPackageSettings = () => wrapper.find({ ref: 'package-settings' });
const findPackagesEnabledInput = () => wrapper.find('[name="project[packages_enabled]"]'); const findPackagesEnabledInput = () => wrapper.find('[name="project[packages_enabled]"]');
const findPagesSettings = () => wrapper.find({ ref: 'pages-settings' }); const findPagesSettings = () => wrapper.find({ ref: 'pages-settings' });
...@@ -275,24 +277,38 @@ describe('Settings Panel', () => { ...@@ -275,24 +277,38 @@ describe('Settings Panel', () => {
it('should show the container registry public note if the visibility level is public and the registry is available', () => { it('should show the container registry public note if the visibility level is public and the registry is available', () => {
wrapper = mountComponent({ wrapper = mountComponent({
currentSettings: { visibilityLevel: visibilityOptions.PUBLIC }, currentSettings: {
visibilityLevel: visibilityOptions.PUBLIC,
containerRegistryAccessLevel: featureAccessLevel.EVERYONE,
},
registryAvailable: true, registryAvailable: true,
}); });
expect(findContainerRegistrySettings().text()).toContain( expect(findContainerRegistryPublicNoteGlSprintfComponent().exists()).toBe(true);
'Note: the container registry is always visible when a project is public', expect(findContainerRegistryPublicNoteGlSprintfComponent().attributes('message')).toContain(
`Note: The container registry is always visible when a project is public and the container registry is set to '%{access_level_description}'`,
); );
}); });
it('should hide the container registry public note if the visibility level is public but the registry is private', () => {
wrapper = mountComponent({
currentSettings: {
visibilityLevel: visibilityOptions.PUBLIC,
containerRegistryAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
},
registryAvailable: true,
});
expect(findContainerRegistryPublicNoteGlSprintfComponent().exists()).toBe(false);
});
it('should hide the container registry public note if the visibility level is private and the registry is available', () => { it('should hide the container registry public note if the visibility level is private and the registry is available', () => {
wrapper = mountComponent({ wrapper = mountComponent({
currentSettings: { visibilityLevel: visibilityOptions.PRIVATE }, currentSettings: { visibilityLevel: visibilityOptions.PRIVATE },
registryAvailable: true, registryAvailable: true,
}); });
expect(findContainerRegistrySettings().text()).not.toContain( expect(findContainerRegistryPublicNoteGlSprintfComponent().exists()).toBe(false);
'Note: the container registry is always visible when a project is public',
);
}); });
it('should enable the container registry input when the repository is enabled', () => { it('should enable the container registry input when the repository is enabled', () => {
...@@ -301,7 +317,7 @@ describe('Settings Panel', () => { ...@@ -301,7 +317,7 @@ describe('Settings Panel', () => {
registryAvailable: true, registryAvailable: true,
}); });
expect(findContainerRegistryEnabledInput().props('disabled')).toBe(false); expect(findContainerRegistryAccessLevelInput().props('disabledInput')).toBe(false);
}); });
it('should disable the container registry input when the repository is disabled', () => { it('should disable the container registry input when the repository is disabled', () => {
...@@ -310,7 +326,7 @@ describe('Settings Panel', () => { ...@@ -310,7 +326,7 @@ describe('Settings Panel', () => {
registryAvailable: true, registryAvailable: true,
}); });
expect(findContainerRegistryEnabledInput().props('disabled')).toBe(true); expect(findContainerRegistryAccessLevelInput().props('disabledInput')).toBe(true);
}); });
it('has label for the toggle', () => { it('has label for the toggle', () => {
...@@ -319,7 +335,7 @@ describe('Settings Panel', () => { ...@@ -319,7 +335,7 @@ describe('Settings Panel', () => {
registryAvailable: true, registryAvailable: true,
}); });
expect(findContainerRegistrySettings().findComponent(GlToggle).props('label')).toBe( expect(findContainerRegistryAccessLevelInput().props('label')).toBe(
settingsPanel.i18n.containerRegistryLabel, settingsPanel.i18n.containerRegistryLabel,
); );
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment