@@ -241,11 +241,11 @@ export default class AuthenticationServiceMock { | |||
handleUpdateGithubRolesMapping: typeof updateGithubRolesMapping = (id, data) => { | |||
this.githubMapping = this.githubMapping.map((mapping) => | |||
mapping.id === id ? { ...mapping, ...data } : mapping | |||
mapping.id === id ? { ...mapping, ...data } : mapping, | |||
); | |||
return Promise.resolve( | |||
this.githubMapping.find((mapping) => mapping.id === id) as GitHubMapping | |||
this.githubMapping.find((mapping) => mapping.id === id) as GitHubMapping, | |||
); | |||
}; | |||
@@ -56,13 +56,17 @@ export function syncNowGithubProvisioning(): Promise<void> { | |||
return post('/api/github_provisioning/sync').catch(throwGlobalError); | |||
} | |||
export function fetchGithubRolesMapping(): Promise<GitHubMapping[]> { | |||
return axios.get('/api/v2/github-permissions-mapping'); | |||
export function fetchGithubRolesMapping() { | |||
return axios | |||
.get<unknown, { githubPermissionsMappings: GitHubMapping[] }>( | |||
'/api/v2/github-permission-mappings', | |||
) | |||
.then((data) => data.githubPermissionsMappings); | |||
} | |||
export function updateGithubRolesMapping( | |||
role: string, | |||
data: Partial<Pick<GitHubMapping, 'permissions'>> | |||
data: Partial<Pick<GitHubMapping, 'permissions'>>, | |||
): Promise<GitHubMapping> { | |||
return axios.patch(`/api/v2/github-permissions-mapping/${role}`, data); | |||
return axios.patch(`/api/v2/github-permission-mappings/${role}`, data); | |||
} |
@@ -40,13 +40,15 @@ initApplication(); | |||
async function initApplication() { | |||
axiosToCatch.interceptors.response.use((response) => response.data); | |||
axiosToCatch.defaults.headers.patch['Content-Type'] = 'application/merge-patch+json'; | |||
axios.defaults.headers.patch['Content-Type'] = 'application/merge-patch+json'; | |||
axios.interceptors.response.use( | |||
(response) => response.data, | |||
(error) => { | |||
const { response } = error; | |||
addGlobalErrorMessage(parseErrorResponse(response)); | |||
return Promise.reject(response); | |||
} | |||
}, | |||
); | |||
const [l10nBundle, currentUser, appState, availableFeatures] = await Promise.all([ | |||
loadL10nBundle(), |
@@ -21,9 +21,9 @@ import React from 'react'; | |||
import ValidationInput, { | |||
ValidationInputErrorPlacement, | |||
} from '../../../../components/controls/ValidationInput'; | |||
import MandatoryFieldMarker from '../../../../components/ui/MandatoryFieldMarker'; | |||
import { ExtendedSettingDefinition, SettingType } from '../../../../types/settings'; | |||
import { getPropertyDescription, getPropertyName, isSecuredDefinition } from '../../utils'; | |||
import AuthenticationFormFieldWrapper from './AuthenticationFormFieldWrapper'; | |||
import AuthenticationMultiValueField from './AuthenticationMultiValuesField'; | |||
import AuthenticationSecuredField from './AuthenticationSecuredField'; | |||
import AuthenticationToggleField from './AuthenticationToggleField'; | |||
@@ -44,58 +44,54 @@ export default function AuthenticationFormField(props: SamlToggleFieldProps) { | |||
const description = getPropertyDescription(definition); | |||
return ( | |||
<div className="settings-definition"> | |||
<div className="settings-definition-left"> | |||
<label className="h3" htmlFor={definition.key}> | |||
{name} | |||
</label> | |||
{mandatory && <MandatoryFieldMarker />} | |||
{definition.description && <div className="markdown small spacer-top">{description}</div>} | |||
</div> | |||
<div className="settings-definition-right big-padded-top display-flex-column"> | |||
{definition.multiValues && ( | |||
<AuthenticationMultiValueField | |||
definition={definition} | |||
settingValue={settingValue as string[]} | |||
onFieldChange={(value) => props.onFieldChange(definition.key, value)} | |||
/> | |||
<AuthenticationFormFieldWrapper | |||
title={name} | |||
defKey={definition.key} | |||
mandatory={mandatory} | |||
description={description} | |||
> | |||
{definition.multiValues && ( | |||
<AuthenticationMultiValueField | |||
definition={definition} | |||
settingValue={settingValue as string[]} | |||
onFieldChange={(value) => props.onFieldChange(definition.key, value)} | |||
/> | |||
)} | |||
{isSecuredDefinition(definition) && ( | |||
<AuthenticationSecuredField | |||
definition={definition} | |||
settingValue={String(settingValue ?? '')} | |||
onFieldChange={props.onFieldChange} | |||
isNotSet={isNotSet} | |||
/> | |||
)} | |||
{!isSecuredDefinition(definition) && definition.type === SettingType.BOOLEAN && ( | |||
<AuthenticationToggleField | |||
definition={definition} | |||
settingValue={settingValue as string | boolean} | |||
onChange={(value) => props.onFieldChange(definition.key, value)} | |||
/> | |||
)} | |||
{!isSecuredDefinition(definition) && | |||
definition.type === undefined && | |||
!definition.multiValues && ( | |||
<ValidationInput | |||
error={error} | |||
errorPlacement={ValidationInputErrorPlacement.Bottom} | |||
isValid={false} | |||
isInvalid={Boolean(error)} | |||
> | |||
<input | |||
className="width-100" | |||
id={definition.key} | |||
maxLength={4000} | |||
name={definition.key} | |||
onChange={(e) => props.onFieldChange(definition.key, e.currentTarget.value)} | |||
type="text" | |||
value={String(settingValue ?? '')} | |||
/> | |||
</ValidationInput> | |||
)} | |||
{isSecuredDefinition(definition) && ( | |||
<AuthenticationSecuredField | |||
definition={definition} | |||
settingValue={String(settingValue ?? '')} | |||
onFieldChange={props.onFieldChange} | |||
isNotSet={isNotSet} | |||
/> | |||
)} | |||
{!isSecuredDefinition(definition) && definition.type === SettingType.BOOLEAN && ( | |||
<AuthenticationToggleField | |||
definition={definition} | |||
settingValue={settingValue as string | boolean} | |||
onChange={(value) => props.onFieldChange(definition.key, value)} | |||
/> | |||
)} | |||
{!isSecuredDefinition(definition) && | |||
definition.type === undefined && | |||
!definition.multiValues && ( | |||
<ValidationInput | |||
error={error} | |||
errorPlacement={ValidationInputErrorPlacement.Bottom} | |||
isValid={false} | |||
isInvalid={Boolean(error)} | |||
> | |||
<input | |||
className="width-100" | |||
id={definition.key} | |||
maxLength={4000} | |||
name={definition.key} | |||
onChange={(e) => props.onFieldChange(definition.key, e.currentTarget.value)} | |||
type="text" | |||
value={String(settingValue ?? '')} | |||
/> | |||
</ValidationInput> | |||
)} | |||
</div> | |||
</div> | |||
</AuthenticationFormFieldWrapper> | |||
); | |||
} |
@@ -0,0 +1,45 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import React, { PropsWithChildren } from 'react'; | |||
import MandatoryFieldMarker from '../../../../components/ui/MandatoryFieldMarker'; | |||
interface Props { | |||
readonly title: string; | |||
readonly description?: string; | |||
readonly defKey?: string; | |||
readonly mandatory?: boolean; | |||
} | |||
export default function AuthenticationFormFieldWrapper(props: PropsWithChildren<Props>) { | |||
const { mandatory = false, title, description, defKey, children } = props; | |||
return ( | |||
<div className="settings-definition"> | |||
<div className="settings-definition-left"> | |||
<label className="h3" htmlFor={defKey}> | |||
{title} | |||
</label> | |||
{mandatory && <MandatoryFieldMarker />} | |||
{description && <div className="markdown small spacer-top">{description}</div>} | |||
</div> | |||
<div className="settings-definition-right big-padded-top display-flex-column">{children}</div> | |||
</div> | |||
); | |||
} |
@@ -34,26 +34,20 @@ import { useGithubRolesMappingQuery } from '../../../../queries/identity-provide | |||
import { GitHubMapping } from '../../../../types/provisioning'; | |||
interface Props { | |||
mapping: GitHubMapping[] | null; | |||
setMapping: React.Dispatch<React.SetStateAction<GitHubMapping[] | null>>; | |||
onClose: () => void; | |||
readonly mapping: GitHubMapping[] | null; | |||
readonly setMapping: React.Dispatch<React.SetStateAction<GitHubMapping[] | null>>; | |||
readonly onClose: () => void; | |||
} | |||
export default function GitHubMappingModal({ mapping, setMapping, onClose }: Props) { | |||
const { data: roles, isLoading } = useGithubRolesMappingQuery(); | |||
const permissions = convertToPermissionDefinitions( | |||
PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE, | |||
'projects_role' | |||
'projects_role', | |||
); | |||
React.useEffect(() => { | |||
if (!mapping && roles) { | |||
setMapping(roles); | |||
} | |||
}, [roles, mapping, setMapping]); | |||
const header = translate( | |||
'settings.authentication.github.configuration.roles_mapping.dialog.title' | |||
'settings.authentication.github.configuration.roles_mapping.dialog.title', | |||
); | |||
return ( | |||
@@ -68,7 +62,7 @@ export default function GitHubMappingModal({ mapping, setMapping, onClose }: Pro | |||
<th scope="col" className="nowrap bordered-bottom sw-pl-[10px] sw-align-middle"> | |||
<b> | |||
{translate( | |||
'settings.authentication.github.configuration.roles_mapping.dialog.roles_column' | |||
'settings.authentication.github.configuration.roles_mapping.dialog.roles_column', | |||
)} | |||
</b> | |||
</th> | |||
@@ -77,14 +71,13 @@ export default function GitHubMappingModal({ mapping, setMapping, onClose }: Pro | |||
key={ | |||
isPermissionDefinitionGroup(permission) ? permission.category : permission.key | |||
} | |||
onSelectPermission={() => {}} | |||
permission={permission} | |||
/> | |||
))} | |||
</tr> | |||
</thead> | |||
<tbody> | |||
{mapping?.map(({ id, roleName, permissions }) => ( | |||
{(mapping ?? roles)?.map(({ id, roleName, permissions }) => ( | |||
<tr key={id}> | |||
<th scope="row" className="nowrap text-middle sw-pl-[10px]"> | |||
<b>{roleName}</b> | |||
@@ -95,11 +88,11 @@ export default function GitHubMappingModal({ mapping, setMapping, onClose }: Pro | |||
checked={value} | |||
onCheck={(newValue) => | |||
setMapping( | |||
mapping.map((item) => | |||
(mapping ?? roles)?.map((item) => | |||
item.id === id | |||
? { ...item, permissions: { ...item.permissions, [key]: newValue } } | |||
: item | |||
) | |||
: item, | |||
) ?? null, | |||
) | |||
} | |||
/> |
@@ -38,6 +38,7 @@ import { AlmKeys } from '../../../../types/alm-settings'; | |||
import { ExtendedSettingDefinition } from '../../../../types/settings'; | |||
import { AuthenticationTabs, DOCUMENTATION_LINK_SUFFIXES } from './Authentication'; | |||
import AuthenticationFormField from './AuthenticationFormField'; | |||
import AuthenticationFormFieldWrapper from './AuthenticationFormFieldWrapper'; | |||
import AutoProvisioningConsent from './AutoProvisionningConsent'; | |||
import ConfigurationForm from './ConfigurationForm'; | |||
import GitHubConfigurationValidity from './GitHubConfigurationValidity'; | |||
@@ -78,12 +79,11 @@ export default function GithubAuthenticationTab(props: GithubAuthenticationProps | |||
hasGithubProvisioningTypeChange, | |||
hasGithubProvisioningConfigChange, | |||
resetJitSetting, | |||
saveGroup, | |||
changeProvisioning, | |||
toggleEnable, | |||
rolesMapping, | |||
setRolesMapping, | |||
saveMapping, | |||
applyAdditionalOptions, | |||
hasLegacyConfiguration, | |||
deleteMutation: { isLoading: isDeleting, mutate: deleteConfiguration }, | |||
} = useGithubConfiguration(definitions); | |||
@@ -106,10 +106,7 @@ export default function GithubAuthenticationTab(props: GithubAuthenticationProps | |||
if (hasGithubProvisioningTypeChange) { | |||
setShowConfirmProvisioningModal(true); | |||
} else { | |||
saveGroup(); | |||
if (newGithubProvisioningStatus ?? githubProvisioningStatus) { | |||
saveMapping(); | |||
} | |||
applyAdditionalOptions(); | |||
} | |||
}; | |||
@@ -197,6 +194,7 @@ export default function GithubAuthenticationTab(props: GithubAuthenticationProps | |||
{enabled ? ( | |||
<div className="display-flex-column spacer-top"> | |||
<RadioCard | |||
className="sw-min-h-0" | |||
label={translate('settings.authentication.form.provisioning_at_login')} | |||
title={translate('settings.authentication.form.provisioning_at_login')} | |||
selected={!(newGithubProvisioningStatus ?? githubProvisioningStatus)} | |||
@@ -246,7 +244,7 @@ export default function GithubAuthenticationTab(props: GithubAuthenticationProps | |||
)} | |||
</RadioCard> | |||
<RadioCard | |||
className="spacer-top" | |||
className="spacer-top sw-min-h-0" | |||
label={translate( | |||
'settings.authentication.github.form.provisioning_with_github', | |||
)} | |||
@@ -287,18 +285,17 @@ export default function GithubAuthenticationTab(props: GithubAuthenticationProps | |||
</p> | |||
{githubProvisioningStatus && <GitHubSynchronisationWarning />} | |||
<div className="sw-flex sw-flex-1 spacer-bottom"> | |||
<Button | |||
className="spacer-top width-30" | |||
onClick={synchronizeNow} | |||
disabled={!canSyncNow} | |||
> | |||
{translate('settings.authentication.github.synchronize_now')} | |||
</Button> | |||
</div> | |||
{(newGithubProvisioningStatus ?? githubProvisioningStatus) && ( | |||
<> | |||
<div className="sw-flex sw-flex-1 spacer-bottom"> | |||
<Button | |||
className="spacer-top width-30" | |||
onClick={synchronizeNow} | |||
disabled={!canSyncNow} | |||
> | |||
{translate('settings.authentication.github.synchronize_now')} | |||
</Button> | |||
</div> | |||
<hr /> | |||
{Object.values(values).map((val) => { | |||
if (!GITHUB_PROVISIONING_FIELDS.includes(val.key)) { | |||
@@ -318,6 +315,23 @@ export default function GithubAuthenticationTab(props: GithubAuthenticationProps | |||
</div> | |||
); | |||
})} | |||
<AuthenticationFormFieldWrapper | |||
title={translate( | |||
'settings.authentication.github.configuration.roles_mapping.title', | |||
)} | |||
description={translate( | |||
'settings.authentication.github.configuration.roles_mapping.description', | |||
)} | |||
> | |||
<Button | |||
className="spacer-top" | |||
onClick={() => setShowMappingModal(true)} | |||
> | |||
{translate( | |||
'settings.authentication.github.configuration.roles_mapping.button_label', | |||
)} | |||
</Button> | |||
</AuthenticationFormFieldWrapper> | |||
</> | |||
)} | |||
</> | |||
@@ -348,12 +362,11 @@ export default function GithubAuthenticationTab(props: GithubAuthenticationProps | |||
)} | |||
</fieldset> | |||
{enabled && ( | |||
<> | |||
<div className="sw-flex sw-gap-2 sw-h-8 sw-items-center"> | |||
<SubmitButton disabled={!hasGithubProvisioningConfigChange}> | |||
{translate('save')} | |||
</SubmitButton> | |||
<ResetButtonLink | |||
className="spacer-left" | |||
onClick={() => { | |||
setProvisioningType(undefined); | |||
resetJitSetting(); | |||
@@ -362,7 +375,11 @@ export default function GithubAuthenticationTab(props: GithubAuthenticationProps | |||
> | |||
{translate('cancel')} | |||
</ResetButtonLink> | |||
</> | |||
<Alert variant="warning" className="sw-w-[300px] sw-mb-0"> | |||
{hasGithubProvisioningConfigChange && | |||
translate('settings.authentication.github.configuration.unsaved_changes')} | |||
</Alert> | |||
</div> | |||
)} | |||
{showConfirmProvisioningModal && ( | |||
<ConfirmModal |
@@ -184,6 +184,7 @@ export default function SamlAuthenticationTab(props: SamlAuthenticationProps) { | |||
{samlEnabled ? ( | |||
<div className="display-flex-column spacer-top"> | |||
<RadioCard | |||
className="sw-min-h-0" | |||
label={translate('settings.authentication.saml.form.provisioning_at_login')} | |||
title={translate('settings.authentication.saml.form.provisioning_at_login')} | |||
selected={!(newScimStatus ?? scimStatus)} | |||
@@ -194,7 +195,7 @@ export default function SamlAuthenticationTab(props: SamlAuthenticationProps) { | |||
</p> | |||
</RadioCard> | |||
<RadioCard | |||
className="spacer-top" | |||
className="spacer-top sw-min-h-0" | |||
label={translate('settings.authentication.saml.form.provisioning_with_scim')} | |||
title={translate('settings.authentication.saml.form.provisioning_with_scim')} | |||
selected={newScimStatus ?? scimStatus} |
@@ -70,7 +70,9 @@ export default function useGithubConfiguration(definitions: ExtendedSettingDefin | |||
newGithubProvisioningStatus !== undefined && | |||
newGithubProvisioningStatus !== githubProvisioningStatus; | |||
const hasGithubProvisioningConfigChange = | |||
some(GITHUB_ADDITIONAL_FIELDS, isValueChange) || hasGithubProvisioningTypeChange || rolesMapping; | |||
some(GITHUB_ADDITIONAL_FIELDS, isValueChange) || | |||
hasGithubProvisioningTypeChange || | |||
rolesMapping; | |||
const resetJitSetting = () => { | |||
GITHUB_ADDITIONAL_FIELDS.forEach((s) => setNewValue(s)); | |||
@@ -78,7 +80,7 @@ export default function useGithubConfiguration(definitions: ExtendedSettingDefin | |||
const { mutate: saveSetting } = useSaveValueMutation(); | |||
const { mutate: saveSettings } = useSaveValuesMutation(); | |||
const { mutate: updateMapping } = useGithubRolesMappingMutation(); | |||
const { mutateAsync: updateMapping } = useGithubRolesMappingMutation(); | |||
const enabled = values[GITHUB_ENABLED_FIELD]?.value === 'true'; | |||
const appId = values[GITHUB_APP_ID_FIELD]?.value as string; | |||
@@ -89,22 +91,20 @@ export default function useGithubConfiguration(definitions: ExtendedSettingDefin | |||
if (hasGithubProvisioningTypeChange) { | |||
await toggleGithubProvisioning.mutateAsync(!!newGithubProvisioningStatus); | |||
} | |||
if (!newGithubProvisioningStatus || !githubProvisioningStatus) { | |||
saveGroup(); | |||
} | |||
if (newGithubProvisioningStatus ?? githubProvisioningStatus) { | |||
saveMapping(); | |||
} | |||
applyAdditionalOptions(); | |||
}; | |||
const saveGroup = () => { | |||
const applyAdditionalOptions = () => { | |||
const newValues = GITHUB_ADDITIONAL_FIELDS.map((settingKey) => values[settingKey]); | |||
saveSettings(newValues); | |||
}; | |||
const saveMapping = () => { | |||
if (rolesMapping) { | |||
updateMapping(rolesMapping); | |||
if (newGithubProvisioningStatus ?? githubProvisioningStatus) { | |||
if (rolesMapping) { | |||
updateMapping(rolesMapping) | |||
.then(() => { | |||
setRolesMapping(null); | |||
}) | |||
.catch(() => {}); | |||
} | |||
} | |||
}; | |||
@@ -132,12 +132,11 @@ export default function useGithubConfiguration(definitions: ExtendedSettingDefin | |||
hasGithubProvisioningTypeChange, | |||
hasGithubProvisioningConfigChange, | |||
changeProvisioning, | |||
saveGroup, | |||
resetJitSetting, | |||
toggleEnable, | |||
rolesMapping, | |||
setRolesMapping, | |||
saveMapping, | |||
applyAdditionalOptions, | |||
hasLegacyConfiguration, | |||
}; | |||
} |
@@ -126,10 +126,11 @@ export function useGithubRolesMappingQuery() { | |||
return useQuery(['identity_provider', 'github_mapping'], fetchGithubRolesMapping, { | |||
staleTime: MAPPING_STALE_TIME, | |||
select: (data) => | |||
data.sort((a, b) => { | |||
const hardcodedValues = ['admin', 'maintain', 'write', 'triage', 'read']; | |||
if (hardcodedValues.includes(a.id) || hardcodedValues.includes(b.id)) { | |||
return hardcodedValues.indexOf(b.id) - hardcodedValues.indexOf(a.id); | |||
[...data].sort((a, b) => { | |||
// Order is reversed to put non-existing roles at the end (their index is -1) | |||
const defaultRoleOrder = ['admin', 'maintain', 'write', 'triage', 'read']; | |||
if (defaultRoleOrder.includes(a.id) || defaultRoleOrder.includes(b.id)) { | |||
return defaultRoleOrder.indexOf(b.id) - defaultRoleOrder.indexOf(a.id); | |||
} | |||
return a.roleName.localeCompare(b.roleName); | |||
}), | |||
@@ -147,12 +148,12 @@ export function useGithubRolesMappingMutation() { | |||
(item) => | |||
!isEqual( | |||
item, | |||
state.find((el) => el.id === item.id) | |||
) | |||
state.find((el) => el.id === item.id), | |||
), | |||
) | |||
: mapping; | |||
return Promise.all( | |||
changedRoles.map((data) => updateGithubRolesMapping(data.id, omit(data, 'id', 'roleName'))) | |||
changedRoles.map((data) => updateGithubRolesMapping(data.id, omit(data, 'id', 'roleName'))), | |||
); | |||
}, | |||
onSuccess: (data) => { | |||
@@ -163,7 +164,7 @@ export function useGithubRolesMappingMutation() { | |||
state.map((item) => { | |||
const changed = data.find((el) => el.id === item.id); | |||
return changed ?? item; | |||
}) | |||
}), | |||
); | |||
} | |||
}, |
@@ -71,8 +71,10 @@ export function useSaveValuesMutation() { | |||
}), | |||
); | |||
}, | |||
onSuccess: () => { | |||
queryClient.invalidateQueries(['settings']); | |||
onSuccess: (data) => { | |||
if (data.length > 0) { | |||
queryClient.invalidateQueries(['settings']); | |||
} | |||
}, | |||
}); | |||
} |
@@ -5247,6 +5247,7 @@ __metadata: | |||
"@typescript-eslint/parser": 5.59.11 | |||
"@wojtekmaj/enzyme-adapter-react-17": 0.8.0 | |||
autoprefixer: 10.4.15 | |||
axios: 1.5.0 | |||
chalk: 4.1.2 | |||
chokidar: 3.5.3 | |||
classnames: 2.3.2 | |||
@@ -5767,6 +5768,17 @@ __metadata: | |||
languageName: node | |||
linkType: hard | |||
"axios@npm:1.5.0": | |||
version: 1.5.0 | |||
resolution: "axios@npm:1.5.0" | |||
dependencies: | |||
follow-redirects: ^1.15.0 | |||
form-data: ^4.0.0 | |||
proxy-from-env: ^1.1.0 | |||
checksum: e7405a5dbbea97760d0e6cd58fecba311b0401ddb4a8efbc4108f5537da9b3f278bde566deb777935a960beec4fa18e7b8353881f2f465e4f2c0e949fead35be | |||
languageName: node | |||
linkType: hard | |||
"axobject-query@npm:^3.1.1": | |||
version: 3.1.1 | |||
resolution: "axobject-query@npm:3.1.1" | |||
@@ -8342,6 +8354,16 @@ __metadata: | |||
languageName: node | |||
linkType: hard | |||
"follow-redirects@npm:^1.15.0": | |||
version: 1.15.2 | |||
resolution: "follow-redirects@npm:1.15.2" | |||
peerDependenciesMeta: | |||
debug: | |||
optional: true | |||
checksum: faa66059b66358ba65c234c2f2a37fcec029dc22775f35d9ad6abac56003268baf41e55f9ee645957b32c7d9f62baf1f0b906e68267276f54ec4b4c597c2b190 | |||
languageName: node | |||
linkType: hard | |||
"for-each@npm:^0.3.3": | |||
version: 0.3.3 | |||
resolution: "for-each@npm:0.3.3" | |||
@@ -11916,6 +11938,13 @@ __metadata: | |||
languageName: node | |||
linkType: hard | |||
"proxy-from-env@npm:^1.1.0": | |||
version: 1.1.0 | |||
resolution: "proxy-from-env@npm:1.1.0" | |||
checksum: ed7fcc2ba0a33404958e34d95d18638249a68c430e30fcb6c478497d72739ba64ce9810a24f53a7d921d0c065e5b78e3822759800698167256b04659366ca4d4 | |||
languageName: node | |||
linkType: hard | |||
"psl@npm:^1.1.33": | |||
version: 1.8.0 | |||
resolution: "psl@npm:1.8.0" |
@@ -1530,8 +1530,12 @@ settings.authentication.github.configuration.validation.details.title=Configurat | |||
settings.authentication.github.configuration.validation.details.valid_label=Valid | |||
settings.authentication.github.configuration.validation.details.invalid_label=Invalid | |||
settings.authentication.github.configuration.validation.details.org_not_found={0} (not found or app not installed) | |||
settings.authentication.github.configuration.roles_mapping.title=Role permission mapping | |||
settings.authentication.github.configuration.roles_mapping.description=When synchronizing users and groups we rely on GitHub user and team roles to assign sonarqube permissions. You can customize how the mapping will be applied. Be aware that the mapping will take effect from the next synchronization. | |||
settings.authentication.github.configuration.roles_mapping.button_label=Edit mapping | |||
settings.authentication.github.configuration.roles_mapping.dialog.title=GitHub Roles Mapping | |||
settings.authentication.github.configuration.roles_mapping.dialog.roles_column=Roles | |||
settings.authentication.github.configuration.unsaved_changes=You have unsaved changes. | |||
# SAML | |||
settings.authentication.form.create.saml=New SAML configuration |