KEYCLOAK-17179 IdP mappers with MultiValued property can't be saved
This commit is contained in:
parent
91865fa93e
commit
07d57ca30f
8 changed files with 282 additions and 16 deletions
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.testsuite.broker.provider;
|
||||||
|
|
||||||
|
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
|
||||||
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Testing IdP mapper with multivalued property
|
||||||
|
*
|
||||||
|
* @author Martin Bartos <mabartos@redhat.com>
|
||||||
|
*/
|
||||||
|
public class MultiValuedTestIdPMapper extends AbstractIdentityProviderMapper {
|
||||||
|
public static final String[] COMPATIBLE_PROVIDERS = {ANY_PROVIDER};
|
||||||
|
|
||||||
|
public static final String PROVIDER_ID = "multi-valued-test-idp-mapper";
|
||||||
|
public static final String VALUES_ATTRIBUTE = "values";
|
||||||
|
|
||||||
|
protected static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
ProviderConfigProperty property;
|
||||||
|
property = new ProviderConfigProperty();
|
||||||
|
property.setName(VALUES_ATTRIBUTE);
|
||||||
|
property.setLabel("Test values");
|
||||||
|
property.setHelpText("Define test values");
|
||||||
|
property.setType(ProviderConfigProperty.MULTIVALUED_STRING_TYPE);
|
||||||
|
configProperties.add(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getCompatibleProviders() {
|
||||||
|
return COMPATIBLE_PROVIDERS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayCategory() {
|
||||||
|
return "Test IdP Mapper";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayType() {
|
||||||
|
return "Test MultiValued Mapper";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHelpText() {
|
||||||
|
return "This is testing IdP mapper with multivalued property";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ProviderConfigProperty> getConfigProperties() {
|
||||||
|
return configProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return PROVIDER_ID;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#
|
||||||
|
# Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||||
|
# and other contributors as indicated by the @author tags.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
org.keycloak.testsuite.broker.provider.MultiValuedTestIdPMapper
|
|
@ -580,9 +580,9 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
||||||
expected.add("hardcoded-user-session-attribute-idp-mapper");
|
expected.add("hardcoded-user-session-attribute-idp-mapper");
|
||||||
expected.add("oidc-hardcoded-role-idp-mapper");
|
expected.add("oidc-hardcoded-role-idp-mapper");
|
||||||
expected.add("hardcoded-attribute-idp-mapper");
|
expected.add("hardcoded-attribute-idp-mapper");
|
||||||
for (String id: mapperIds) {
|
expected.add("multi-valued-test-idp-mapper");
|
||||||
expected.add(id);
|
expected.addAll(Arrays.asList(mapperIds));
|
||||||
}
|
|
||||||
Assert.assertEquals("mapperTypes", expected, mapperTypes.keySet());
|
Assert.assertEquals("mapperTypes", expected, mapperTypes.keySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.keycloak.testsuite.console.page.idp;
|
package org.keycloak.testsuite.console.page.idp.mappers;
|
||||||
|
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
import org.keycloak.testsuite.console.page.AdminConsoleCreate;
|
import org.keycloak.testsuite.console.page.AdminConsoleCreate;
|
|
@ -15,7 +15,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.keycloak.testsuite.console.page.idp;
|
package org.keycloak.testsuite.console.page.idp.mappers;
|
||||||
|
|
||||||
import org.keycloak.testsuite.page.Form;
|
import org.keycloak.testsuite.page.Form;
|
||||||
import org.openqa.selenium.WebElement;
|
import org.openqa.selenium.WebElement;
|
||||||
|
@ -34,6 +34,9 @@ public class IdentityProviderMapperForm extends Form {
|
||||||
@FindBy(id = "syncMode")
|
@FindBy(id = "syncMode")
|
||||||
private Select syncMode;
|
private Select syncMode;
|
||||||
|
|
||||||
|
@FindBy(id = "mapperTypeCreate")
|
||||||
|
private Select mapperType;
|
||||||
|
|
||||||
public void setName(final String value) {
|
public void setName(final String value) {
|
||||||
setTextInputValue(name, value);
|
setTextInputValue(name, value);
|
||||||
}
|
}
|
||||||
|
@ -45,4 +48,12 @@ public class IdentityProviderMapperForm extends Form {
|
||||||
public String syncMode() {
|
public String syncMode() {
|
||||||
return syncMode.getFirstSelectedOption().getText();
|
return syncMode.getFirstSelectedOption().getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setMapperType(final String value) {
|
||||||
|
mapperType.selectByVisibleText(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMapperType() {
|
||||||
|
return mapperType.getFirstSelectedOption().getText();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.testsuite.console.page.idp.mappers;
|
||||||
|
|
||||||
|
import org.openqa.selenium.WebElement;
|
||||||
|
import org.openqa.selenium.support.FindBy;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import static org.keycloak.testsuite.util.UIUtils.getTextInputValue;
|
||||||
|
import static org.keycloak.testsuite.util.UIUtils.setTextInputValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author mabartos
|
||||||
|
*/
|
||||||
|
public class MultivaluedStringProperty {
|
||||||
|
|
||||||
|
@FindBy(xpath = "//input[@ng-model='config[option.name][i]']")
|
||||||
|
private List<WebElement> items;
|
||||||
|
|
||||||
|
@FindBy(xpath = "//button[@data-ng-click='deleteValueFromMultivalued(option.name, $index)']")
|
||||||
|
private List<WebElement> minusButtons;
|
||||||
|
|
||||||
|
@FindBy(xpath = "//button[@data-ng-click='addValueToMultivalued(option.name)']")
|
||||||
|
private WebElement plusButton;
|
||||||
|
|
||||||
|
public boolean isPresent() {
|
||||||
|
try {
|
||||||
|
return plusButton.isDisplayed() && items != null && !items.isEmpty();
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clickAddItem() {
|
||||||
|
plusButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<WebElement> getItems() {
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getItem(int index) {
|
||||||
|
validateIndex(index);
|
||||||
|
return getTextInputValue(getItems().get(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void editItem(int index, String item) {
|
||||||
|
validateIndex(index);
|
||||||
|
setTextInputValue(getItems().get(index), item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addItem(String item) {
|
||||||
|
clickAddItem();
|
||||||
|
|
||||||
|
final List<WebElement> items = getItems();
|
||||||
|
WebElement webElement = items.get(items.size() - 1);
|
||||||
|
setTextInputValue(webElement, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeItem(int index) {
|
||||||
|
validateIndex(index);
|
||||||
|
if (index == getItems().size() - 1) {
|
||||||
|
editItem(index, "");
|
||||||
|
} else {
|
||||||
|
minusButtons.get(index).click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateIndex(int index) {
|
||||||
|
if (index >= getItems().size()) throw new AssertionError("Input with index: " + index + " does not exist.");
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,18 +20,20 @@ package org.keycloak.testsuite.console.idp;
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.testsuite.console.page.idp.mappers.MultivaluedStringProperty;
|
||||||
import org.keycloak.testsuite.console.AbstractConsoleTest;
|
import org.keycloak.testsuite.console.AbstractConsoleTest;
|
||||||
import org.keycloak.testsuite.console.page.idp.CreateIdentityProvider;
|
import org.keycloak.testsuite.console.page.idp.CreateIdentityProvider;
|
||||||
import org.keycloak.testsuite.console.page.idp.CreateIdentityProviderMapper;
|
import org.keycloak.testsuite.console.page.idp.mappers.CreateIdentityProviderMapper;
|
||||||
import org.keycloak.testsuite.console.page.idp.IdentityProvider;
|
import org.keycloak.testsuite.console.page.idp.IdentityProvider;
|
||||||
import org.keycloak.testsuite.console.page.idp.IdentityProviders;
|
import org.keycloak.testsuite.console.page.idp.IdentityProviders;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.keycloak.testsuite.util.UIUtils.refreshPageAndWaitForLoad;
|
import static org.keycloak.testsuite.util.UIUtils.refreshPageAndWaitForLoad;
|
||||||
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
|
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
|
||||||
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -52,6 +54,9 @@ public class IdentityProviderTest extends AbstractConsoleTest {
|
||||||
@Page
|
@Page
|
||||||
private CreateIdentityProviderMapper createIdentityProviderMapperPage;
|
private CreateIdentityProviderMapper createIdentityProviderMapperPage;
|
||||||
|
|
||||||
|
@Page
|
||||||
|
private MultivaluedStringProperty multiStringPropertyForm;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeIdentityProviderTest() {
|
public void beforeIdentityProviderTest() {
|
||||||
identityProvidersPage.navigateTo();
|
identityProvidersPage.navigateTo();
|
||||||
|
@ -128,6 +133,57 @@ public class IdentityProviderTest extends AbstractConsoleTest {
|
||||||
assertMapperSyncModeIsSetToImport();
|
assertMapperSyncModeIsSetToImport();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createIdentityProviderCustomMapper() {
|
||||||
|
createIdentityProviderPage.setProviderId("google");
|
||||||
|
identityProviderPage.setIds("google", "google");
|
||||||
|
|
||||||
|
identityProvidersPage.addProvider("google");
|
||||||
|
assertCurrentUrlEquals(createIdentityProviderPage);
|
||||||
|
|
||||||
|
createIdentityProviderPage.form().setClientId("test-google");
|
||||||
|
createIdentityProviderPage.form().setClientSecret("secret");
|
||||||
|
|
||||||
|
createIdentityProviderPage.form().save();
|
||||||
|
assertAlertSuccess();
|
||||||
|
refreshPageAndWaitForLoad();
|
||||||
|
assertCurrentUrlEquals(identityProviderPage);
|
||||||
|
|
||||||
|
identityProviderPage.form().createMapper();
|
||||||
|
createIdentityProviderMapperPage.setIdp("google");
|
||||||
|
assertCurrentUrlEquals(createIdentityProviderMapperPage);
|
||||||
|
createIdentityProviderMapperPage.form().setName("Multivalued Map");
|
||||||
|
createIdentityProviderMapperPage.form().setSyncMode("import");
|
||||||
|
createIdentityProviderMapperPage.form().setMapperType("Test MultiValued Mapper");
|
||||||
|
|
||||||
|
assertThat(multiStringPropertyForm.isPresent(), is(true));
|
||||||
|
assertThat(multiStringPropertyForm.getItems().size(), is(1));
|
||||||
|
|
||||||
|
multiStringPropertyForm.editItem(0, "firstValue");
|
||||||
|
assertThat(multiStringPropertyForm.getItem(0), is("firstValue"));
|
||||||
|
|
||||||
|
multiStringPropertyForm.addItem("second");
|
||||||
|
assertThat(multiStringPropertyForm.getItems().size(), is(2));
|
||||||
|
|
||||||
|
multiStringPropertyForm.editItem(1, "secondValue");
|
||||||
|
assertThat(multiStringPropertyForm.getItem(1), is("secondValue"));
|
||||||
|
|
||||||
|
multiStringPropertyForm.addItem("third");
|
||||||
|
assertThat(multiStringPropertyForm.getItems().size(), is(3));
|
||||||
|
|
||||||
|
multiStringPropertyForm.removeItem(1);
|
||||||
|
assertThat(multiStringPropertyForm.getItems().size(), is(2));
|
||||||
|
assertThat(multiStringPropertyForm.getItem(1), is("third"));
|
||||||
|
|
||||||
|
createIdentityProviderMapperPage.form().save();
|
||||||
|
assertAlertSuccess();
|
||||||
|
|
||||||
|
// add empty item
|
||||||
|
assertThat(multiStringPropertyForm.getItems().size(), is(3));
|
||||||
|
refreshPageAndWaitForLoad();
|
||||||
|
assertThat(multiStringPropertyForm.getItems().size(), is(3));
|
||||||
|
}
|
||||||
|
|
||||||
private void assertMapperSyncModeIsSetToImport() {
|
private void assertMapperSyncModeIsSetToImport() {
|
||||||
assertEquals("import", createIdentityProviderMapperPage.form().syncMode());
|
assertEquals("import", createIdentityProviderMapperPage.form().syncMode());
|
||||||
}
|
}
|
||||||
|
|
|
@ -2198,14 +2198,19 @@ module.controller('IdentityProviderMapperListCtrl', function($scope, realm, iden
|
||||||
$scope.mappers = mappers;
|
$scope.mappers = mappers;
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('IdentityProviderMapperCtrl', function($scope, realm, identityProvider, mapperTypes, mapper, IdentityProviderMapper, Notifications, Dialog, $location) {
|
module.controller('IdentityProviderMapperCtrl', function ($scope, realm, identityProvider, mapperTypes, mapper, IdentityProviderMapper, Notifications, Dialog, ComponentUtils, $location) {
|
||||||
$scope.realm = realm;
|
$scope.realm = realm;
|
||||||
$scope.identityProvider = identityProvider;
|
$scope.identityProvider = identityProvider;
|
||||||
$scope.create = false;
|
$scope.create = false;
|
||||||
$scope.mapper = angular.copy(mapper);
|
|
||||||
$scope.changed = false;
|
$scope.changed = false;
|
||||||
$scope.mapperType = mapperTypes[mapper.identityProviderMapper];
|
$scope.mapperType = mapperTypes[mapper.identityProviderMapper];
|
||||||
$scope.$watch(function() {
|
|
||||||
|
ComponentUtils.convertAllMultivaluedStringValuesToList($scope.mapperType.properties, mapper.config);
|
||||||
|
ComponentUtils.addLastEmptyValueToMultivaluedLists($scope.mapperType.properties, mapper.config);
|
||||||
|
|
||||||
|
$scope.mapper = angular.copy(mapper);
|
||||||
|
|
||||||
|
$scope.$watch(function () {
|
||||||
return $location.path();
|
return $location.path();
|
||||||
}, function() {
|
}, function() {
|
||||||
$scope.path = $location.path().substring(1).split("/");
|
$scope.path = $location.path().substring(1).split("/");
|
||||||
|
@ -2218,12 +2223,16 @@ module.controller('IdentityProviderMapperCtrl', function($scope, realm, identit
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
$scope.save = function() {
|
$scope.save = function() {
|
||||||
|
let mapperCopy = angular.copy($scope.mapper);
|
||||||
|
ComponentUtils.convertAllListValuesToMultivaluedString($scope.mapperType.properties, mapperCopy.config);
|
||||||
|
|
||||||
IdentityProviderMapper.update({
|
IdentityProviderMapper.update({
|
||||||
realm : realm.realm,
|
realm : realm.realm,
|
||||||
alias: identityProvider.alias,
|
alias : identityProvider.alias,
|
||||||
mapperId : mapper.id
|
mapperId : mapper.id
|
||||||
}, $scope.mapper, function() {
|
}, mapperCopy, function () {
|
||||||
$scope.changed = false;
|
$scope.changed = false;
|
||||||
|
ComponentUtils.addLastEmptyValueToMultivaluedLists($scope.mapperType.properties, $scope.mapper.config);
|
||||||
mapper = angular.copy($scope.mapper);
|
mapper = angular.copy($scope.mapper);
|
||||||
$location.url("/realms/" + realm.realm + '/identity-provider-mappers/' + identityProvider.alias + "/mappers/" + mapper.id);
|
$location.url("/realms/" + realm.realm + '/identity-provider-mappers/' + identityProvider.alias + "/mappers/" + mapper.id);
|
||||||
Notifications.success("Your changes have been saved.");
|
Notifications.success("Your changes have been saved.");
|
||||||
|
@ -2251,7 +2260,7 @@ module.controller('IdentityProviderMapperCtrl', function($scope, realm, identit
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('IdentityProviderMapperCreateCtrl', function($scope, realm, identityProvider, mapperTypes, IdentityProviderMapper, Notifications, Dialog, $location) {
|
module.controller('IdentityProviderMapperCreateCtrl', function ($scope, realm, identityProvider, mapperTypes, IdentityProviderMapper, Notifications, Dialog, ComponentUtils, $location) {
|
||||||
$scope.realm = realm;
|
$scope.realm = realm;
|
||||||
$scope.identityProvider = identityProvider;
|
$scope.identityProvider = identityProvider;
|
||||||
$scope.create = true;
|
$scope.create = true;
|
||||||
|
@ -2268,11 +2277,15 @@ module.controller('IdentityProviderMapperCreateCtrl', function($scope, realm, id
|
||||||
$scope.path = $location.path().substring(1).split("/");
|
$scope.path = $location.path().substring(1).split("/");
|
||||||
});
|
});
|
||||||
|
|
||||||
$scope.save = function() {
|
$scope.save = function () {
|
||||||
$scope.mapper.identityProviderMapper = $scope.mapperType.id;
|
$scope.mapper.identityProviderMapper = $scope.mapperType.id;
|
||||||
|
let copyMapper = angular.copy($scope.mapper);
|
||||||
|
ComponentUtils.convertAllListValuesToMultivaluedString($scope.mapperType.properties, copyMapper.config);
|
||||||
|
|
||||||
IdentityProviderMapper.save({
|
IdentityProviderMapper.save({
|
||||||
realm : realm.realm, alias: identityProvider.alias
|
realm : realm.realm,
|
||||||
}, $scope.mapper, function(data, headers) {
|
alias : identityProvider.alias
|
||||||
|
}, copyMapper, function (data, headers) {
|
||||||
var l = headers().location;
|
var l = headers().location;
|
||||||
var id = l.substring(l.lastIndexOf("/") + 1);
|
var id = l.substring(l.lastIndexOf("/") + 1);
|
||||||
$location.url("/realms/" + realm.realm + '/identity-provider-mappers/' + identityProvider.alias + "/mappers/" + id);
|
$location.url("/realms/" + realm.realm + '/identity-provider-mappers/' + identityProvider.alias + "/mappers/" + id);
|
||||||
|
|
Loading…
Reference in a new issue