From 6db9468e6a0fb88e7edcc1d0a40e50e0173c7d76 Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:56:15 +0530 Subject: [PATCH 01/23] add discoverable groups attribute into service provider model --- .../common/model/DiscoverableGroup.java | 120 +++++++++++++ .../common/model/ServiceProvider.java | 38 ++++ .../test/model/DiscoverableGroupTest.java | 168 ++++++++++++++++++ .../model/test/model/ServiceProviderTest.java | 89 ++++++++++ .../src/test/resources/testng.xml | 2 + 5 files changed, 417 insertions(+) create mode 100644 components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/DiscoverableGroup.java create mode 100644 components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/DiscoverableGroupTest.java create mode 100644 components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/ServiceProviderTest.java diff --git a/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/DiscoverableGroup.java b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/DiscoverableGroup.java new file mode 100644 index 000000000000..84e423e975ed --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/DiscoverableGroup.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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.wso2.carbon.identity.application.common.model; + +import org.apache.axiom.om.OMElement; +import org.apache.commons.lang.StringUtils; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * The list of groups through which the application can be discovered in my account. + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlRootElement(name = "DiscoverableGroup") +public class DiscoverableGroup implements Serializable { + + private static final String USER_STORE = "UserStore"; + private static final String GROUP = "Group"; + + @XmlElement(name = USER_STORE) + private String userStore; + + @XmlElement(name = GROUP) + private String[] groups; + + /** + * Creates an instance of the DiscoverableGroup class by parsing an OMElement. + * + * @param discoverableGroupOM The OMElement used to parse and build the DiscoverableGroup object. + * @return A new DiscoverableGroup object populated with data from the OMElement. + */ + public static DiscoverableGroup build(OMElement discoverableGroupOM) { + + DiscoverableGroup discoverableGroup = new DiscoverableGroup(); + Iterator iter = discoverableGroupOM.getChildElements(); + List groupList = new ArrayList<>(); + + while (iter.hasNext()) { + OMElement element = (OMElement) iter.next(); + String elementName = element.getLocalName(); + + if (USER_STORE.equals(elementName) && StringUtils.isNotBlank(element.getText())) { + discoverableGroup.setUserStore(element.getText()); + } else if (GROUP.equals(elementName) && StringUtils.isNotBlank(element.getText())) { + groupList.add(element.getText()); + } + } + + if (discoverableGroup.getUserStore() == null || groupList.isEmpty()) { + return null; + } + + discoverableGroup.setGroups(groupList.toArray(new String[0])); + return discoverableGroup; + } + + /** + * Get the list of discoverable group IDs from the current user store. + * + * @return The list of group IDs. + */ + public String[] getGroups() { + + return groups; + } + + /** + * Set the list of discoverable group IDs for the current user store. + * + * @param groups The list of group IDs. + */ + public void setGroups(String[] groups) { + + this.groups = groups; + } + + /** + * Get the user store name. + * + * @return The user store name. + */ + public String getUserStore() { + + return userStore; + } + + /** + * Set the user store name. + * + * @param userStore The user store name. + */ + public void setUserStore(String userStore) { + + this.userStore = userStore; + } +} diff --git a/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/ServiceProvider.java b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/ServiceProvider.java index cfc86bada292..39491a7786da 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/ServiceProvider.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/ServiceProvider.java @@ -59,6 +59,8 @@ public class ServiceProvider implements Serializable { private static final String ASSOCIATED_ROLES_CONFIG = "AssociatedRolesConfig"; private static final String IS_API_BASED_AUTHENTICATION_ENABLED = "IsAPIBasedAuthenticationEnabled"; private static final String TRUSTED_APP_METADATA = "TrustedAppMetadata"; + private static final String DISCOVERABLE_GROUPS = "DiscoverableGroups"; + private static final String DISCOVERABLE_GROUP = "DiscoverableGroup"; @XmlTransient @JsonIgnore @@ -132,6 +134,10 @@ public class ServiceProvider implements Serializable { @XmlElement(name = "IsDiscoverable") private boolean isDiscoverable; + @XmlElementWrapper(name = DISCOVERABLE_GROUPS) + @XmlElement(name = DISCOVERABLE_GROUP) + private DiscoverableGroup[] discoverableGroups; + @IgnoreNullElement @XmlElement(name = TEMPLATE_ID) private String templateId; @@ -305,6 +311,18 @@ public static ServiceProvider build(OMElement serviceProviderOM) { } else { serviceProvider.setApplicationEnabled(!"false".equals(element.getText())); } + } else if (DISCOVERABLE_GROUPS.equals(elementName)) { + Iterator discoverableGroupIter = element.getChildElements(); + List discoverableGroupList = new ArrayList<>(); + + while (discoverableGroupIter.hasNext()) { + OMElement discoverableGroupElement = (OMElement) discoverableGroupIter.next(); + discoverableGroupList.add(DiscoverableGroup.build(discoverableGroupElement)); + } + + if (!discoverableGroupList.isEmpty()) { + serviceProvider.setDiscoverableGroups(discoverableGroupList.toArray(new DiscoverableGroup[0])); + } } } @@ -602,6 +620,26 @@ public void setDiscoverable(boolean discoverable) { isDiscoverable = discoverable; } + /** + * Retrieve the list of groups for which the application has been granted discoverable access. + * + * @return The list of discoverable groups. + */ + public DiscoverableGroup[] getDiscoverableGroups() { + + return discoverableGroups; + } + + /** + * Set the list of groups for which the application has been granted discoverable access. + * + * @param discoverableGroups The list of discoverable groups. + */ + public void setDiscoverableGroups(DiscoverableGroup[] discoverableGroups) { + + this.discoverableGroups = discoverableGroups; + } + public String getTemplateId() { return templateId; diff --git a/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/DiscoverableGroupTest.java b/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/DiscoverableGroupTest.java new file mode 100644 index 000000000000..68793bbb1409 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/DiscoverableGroupTest.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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.wso2.carbon.identity.application.common.model.test.model; + +import org.apache.axiom.om.OMElement; +import org.apache.axiom.om.OMXMLBuilderFactory; +import org.testng.annotations.Test; +import org.wso2.carbon.identity.application.common.model.DiscoverableGroup; + +import java.io.StringReader; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +/** + * Unit tests for the DiscoverableGroup. + */ +@Test +public class DiscoverableGroupTest { + + @Test(description = "Test the creation of the DiscoverableGroup model using XML content") + public void testBuildDiscoverableGroupInstanceFromXML() { + + final String discoverableGroupsXML = "\n" + + " test-domain\n" + + " test-group-id-1\n" + + " test-group-id-2\n" + + " test-group-id-3\n" + + ""; + + OMElement rootElement = + OMXMLBuilderFactory.createOMBuilder(new StringReader(discoverableGroupsXML)).getDocumentElement(); + DiscoverableGroup discoverableGroup = DiscoverableGroup.build(rootElement); + assertEquals(discoverableGroup.getUserStore(), "test-domain", + "The user store name does not match the user store name specified in the provided XML content"); + assertEquals(discoverableGroup.getGroups().length, 3, + "The group list does not match the group list specified in the provided XML content"); + assertEquals(discoverableGroup.getGroups()[0], "test-group-id-1", + "The group id does not match the group id specified in the provided XML content"); + assertEquals(discoverableGroup.getGroups()[1], "test-group-id-2", + "The group id does not match the group id specified in the provided XML content"); + assertEquals(discoverableGroup.getGroups()[2], "test-group-id-3", + "The group id does not match the group id specified in the provided XML content"); + } + + @Test(description = "Test the creation of the DiscoverableGroup model using XML content with empty user store") + public void testBuildDiscoverableGroupInstanceFromXMLWithEmptyUserStore() { + + final String discoverableGroupsXML = "\n" + + " \n" + + " test-group-id-1\n" + + " test-group-id-2\n" + + " test-group-id-3\n" + + ""; + + OMElement rootElement = + OMXMLBuilderFactory.createOMBuilder(new StringReader(discoverableGroupsXML)).getDocumentElement(); + DiscoverableGroup discoverableGroup = DiscoverableGroup.build(rootElement); + assertNull(discoverableGroup); + } + + @Test(description = "Test the creation of the DiscoverableGroup model using XML content with whitespace" + + " user store") + public void testBuildDiscoverableGroupInstanceFromXMLWithWhitespaceUserStore() { + + final String discoverableGroupsXML = "\n" + + " \n" + + " test-group-id-1\n" + + " test-group-id-2\n" + + " test-group-id-3\n" + + ""; + + OMElement rootElement = + OMXMLBuilderFactory.createOMBuilder(new StringReader(discoverableGroupsXML)).getDocumentElement(); + DiscoverableGroup discoverableGroup = DiscoverableGroup.build(rootElement); + assertNull(discoverableGroup); + } + + @Test(description = "Test the creation of the DiscoverableGroup model using XML content with empty group") + public void testBuildDiscoverableGroupInstanceFromXMLWithEmptyGroup() { + + final String discoverableGroupsXML = "\n" + + " test-domain\n" + + " test-group-id-1\n" + + " \n" + + " test-group-id-3\n" + + ""; + + OMElement rootElement = + OMXMLBuilderFactory.createOMBuilder(new StringReader(discoverableGroupsXML)).getDocumentElement(); + DiscoverableGroup discoverableGroup = DiscoverableGroup.build(rootElement); + assertEquals(discoverableGroup.getUserStore(), "test-domain", + "The user store name does not match the user store name specified in the provided XML content"); + assertEquals(discoverableGroup.getGroups().length, 2, + "The group list does not match the valid group list specified in the provided XML content"); + assertEquals(discoverableGroup.getGroups()[0], "test-group-id-1", + "The group id does not match the group id specified in the provided XML content"); + assertEquals(discoverableGroup.getGroups()[1], "test-group-id-3", + "The group id does not match the group id specified in the provided XML content"); + } + + @Test(description = "Test the creation of the DiscoverableGroup model using XML content with whitespace group") + public void testBuildDiscoverableGroupInstanceFromXMLWithWhitespaceGroup() { + + final String discoverableGroupsXML = "\n" + + " test-domain\n" + + " test-group-id-1\n" + + " \n" + + " test-group-id-3\n" + + ""; + + OMElement rootElement = + OMXMLBuilderFactory.createOMBuilder(new StringReader(discoverableGroupsXML)).getDocumentElement(); + DiscoverableGroup discoverableGroup = DiscoverableGroup.build(rootElement); + assertEquals(discoverableGroup.getUserStore(), "test-domain", + "The user store name does not match the user store name specified in the provided XML content"); + assertEquals(discoverableGroup.getGroups().length, 2, + "The group list does not match the valid group list specified in the provided XML content"); + assertEquals(discoverableGroup.getGroups()[0], "test-group-id-1", + "The group id does not match the group id specified in the provided XML content"); + assertEquals(discoverableGroup.getGroups()[1], "test-group-id-3", + "The group id does not match the group id specified in the provided XML content"); + } + + @Test(description = "Test the creation of the DiscoverableGroup model using XML content without user store") + public void testBuildDiscoverableGroupInstanceFromXMLWithoutUserStore() { + + final String discoverableGroupsXML = "\n" + + " test-group-id-1\n" + + " test-group-id-2\n" + + " test-group-id-3\n" + + ""; + + OMElement rootElement = + OMXMLBuilderFactory.createOMBuilder(new StringReader(discoverableGroupsXML)).getDocumentElement(); + DiscoverableGroup discoverableGroup = DiscoverableGroup.build(rootElement); + assertNull(discoverableGroup); + } + + @Test(description = "Test the creation of the DiscoverableGroup model using XML content without groups") + public void testBuildDiscoverableGroupInstanceFromXMLWithoutGroups() { + + final String discoverableGroupsXML = "\n" + + " test-domain\n" + + ""; + + OMElement rootElement = + OMXMLBuilderFactory.createOMBuilder(new StringReader(discoverableGroupsXML)).getDocumentElement(); + DiscoverableGroup discoverableGroup = DiscoverableGroup.build(rootElement); + assertNull(discoverableGroup); + } +} diff --git a/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/ServiceProviderTest.java b/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/ServiceProviderTest.java new file mode 100644 index 000000000000..e9ee9f6614f6 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/ServiceProviderTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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.wso2.carbon.identity.application.common.model.test.model; + +import org.apache.axiom.om.OMElement; +import org.apache.axiom.om.OMXMLBuilderFactory; +import org.testng.annotations.Test; +import org.wso2.carbon.identity.application.common.model.DiscoverableGroup; +import org.wso2.carbon.identity.application.common.model.ServiceProvider; + +import java.io.StringReader; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +/** + * Unit tests for the ServiceProvider model. + */ +@Test +public class ServiceProviderTest { + + @Test(description = "Test the construction of the DiscoverableGroups list from the service provider as XML" + + " content") + public void testDiscoverableGroupsList() { + + final String serviceProviderXML = "\n" + + " TestApp\n" + + " \n" + + " \n" + + " test-domain-1\n" + + " test-id-1\n" + + " test-id-2\n" + + " \n" + + " \n" + + " test-domain-2\n" + + " test-id-3\n" + + " test-id-4\n" + + " \n" + + " \n" + + ""; + + OMElement rootElement = + OMXMLBuilderFactory.createOMBuilder(new StringReader(serviceProviderXML)) + .getDocumentElement(); + ServiceProvider serviceProvider = ServiceProvider.build(rootElement); + DiscoverableGroup[] discoverableGroups = serviceProvider.getDiscoverableGroups(); + assertEquals(discoverableGroups.length, 2, + "The discoverable group list does not match the discoverable group list specified in the provided" + + " XML content"); + assertEquals(discoverableGroups[0].getUserStore(), "test-domain-1", + "The user store name does not match the user store name specified in the provided XML content"); + assertEquals(discoverableGroups[0].getGroups(), new String[] {"test-id-1", "test-id-2"}, + "The group list does not match the group list specified in the provided XML content"); + assertEquals(discoverableGroups[1].getUserStore(), "test-domain-2", + "The user store name does not match the user store name specified in the provided XML content"); + assertEquals(discoverableGroups[1].getGroups(), new String[] {"test-id-3", "test-id-4"}, + "The group list does not match the group list specified in the provided XML content"); + } + + @Test(description = "Test the construction of the empty DiscoverableGroups list from the service provider" + + " as XML content") + public void testEmptyDiscoverableGroupsList() { + + final String serviceProviderXML = "\n" + + " TestApp\n" + + ""; + + OMElement rootElement = + OMXMLBuilderFactory.createOMBuilder(new StringReader(serviceProviderXML)).getDocumentElement(); + ServiceProvider serviceProvider = ServiceProvider.build(rootElement); + assertNull(serviceProvider.getDiscoverableGroups()); + } +} diff --git a/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/resources/testng.xml b/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/resources/testng.xml index 5e099dcc02ab..6db693e843c4 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/resources/testng.xml +++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/resources/testng.xml @@ -24,6 +24,8 @@ + + From f980f54a4cc1d5d034adc1750d2f60862d926fd8 Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Tue, 14 Jan 2025 22:57:20 +0530 Subject: [PATCH 02/23] add a new table to keep the app group association --- .../resources/dbscripts/db2.sql | 21 +++++++++++++++++++ .../resources/dbscripts/h2.sql | 10 +++++++++ .../resources/dbscripts/mssql.sql | 11 ++++++++++ .../resources/dbscripts/mysql-cluster.sql | 10 +++++++++ .../resources/dbscripts/mysql.sql | 10 +++++++++ .../resources/dbscripts/oracle.sql | 21 +++++++++++++++++++ .../resources/dbscripts/oracle_rac.sql | 20 ++++++++++++++++++ .../resources/dbscripts/postgresql.sql | 13 ++++++++++++ 8 files changed, 116 insertions(+) diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/db2.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/db2.sql index 81a9cf6224b0..28361001b2a9 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/db2.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/db2.sql @@ -2160,6 +2160,27 @@ REFERENCING NEW AS NEW FOR EACH ROW MODE DB2SQL END / +CREATE TABLE APP_GROUP_ASSOCIATION ( + ID INTEGER NOT NULL, + APP_ID CHAR(36) NOT NULL, + GROUP_ID VARCHAR (255) NOT NULL, + UM_DOMAIN_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, + CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, UM_DOMAIN_ID)) +/ +CREATE SEQUENCE APP_GROUP_ASSOCIATION_SEQ START WITH 1 INCREMENT BY 1 NOCACHE +/ +CREATE TRIGGER APP_GROUP_ASSOCIATION_TRIG NO CASCADE + BEFORE INSERT + ON APP_GROUP_ASSOCIATION + REFERENCING NEW AS NEW + FOR EACH ROW MODE DB2SQL + BEGIN ATOMIC + SET (NEW.ID) = (NEXTVAL FOR APP_GROUP_ASSOCIATION_SEQ); + END +/ + -- --------------------------- INDEX CREATION ----------------------------- -- IDN_OAUTH2_ACCESS_TOKEN -- CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED) diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql index eddc94592760..d3652d8babab 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql @@ -1413,6 +1413,16 @@ CREATE TABLE IF NOT EXISTS IDN_RULE_REFERENCES ( FOREIGN KEY (RULE_ID) REFERENCES IDN_RULE(ID) ON DELETE CASCADE ); +CREATE TABLE IF NOT EXISTS APP_GROUP_ASSOCIATION ( + ID INTEGER NOT NULL AUTO_INCREMENT, + APP_ID CHAR (36) NOT NULL, + GROUP_ID VARCHAR (255) NOT NULL, + UM_DOMAIN_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, + CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, UM_DOMAIN_ID) +); + -- --------------------------- INDEX CREATION ----------------------------- -- IDN_OAUTH2_ACCESS_TOKEN -- CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED); diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mssql.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mssql.sql index aa9fcae8bfe3..09b99e982c6f 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mssql.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mssql.sql @@ -1565,6 +1565,17 @@ CREATE TABLE IDN_RULE_REFERENCES ( FOREIGN KEY (RULE_ID) REFERENCES IDN_RULE(ID) ON DELETE CASCADE ); +IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[APP_GROUP_ASSOCIATION]') AND TYPE IN (N'U')) +CREATE TABLE APP_GROUP_ASSOCIATION ( + ID INTEGER NOT NULL IDENTITY, + APP_ID CHAR (36) NOT NULL, + GROUP_ID VARCHAR (255) NOT NULL, + UM_DOMAIN_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, + CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, UM_DOMAIN_ID) +); + -- --------------------------- INDEX CREATION ----------------------------- -- IDN_OAUTH2_ACCESS_TOKEN -- CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED); diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql-cluster.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql-cluster.sql index fd89394249b2..58180a202f03 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql-cluster.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql-cluster.sql @@ -1576,6 +1576,16 @@ CREATE TABLE IF NOT EXISTS IDN_RULE_REFERENCES ( FOREIGN KEY (RULE_ID) REFERENCES IDN_RULE(ID) ON DELETE CASCADE )ENGINE NDB; +CREATE TABLE IF NOT EXISTS APP_GROUP_ASSOCIATION ( + ID INTEGER NOT NULL AUTO_INCREMENT, + APP_ID CHAR (36) NOT NULL, + GROUP_ID VARCHAR (255) NOT NULL, + UM_DOMAIN_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, + CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, UM_DOMAIN_ID) +)ENGINE NDB; + -- --------------------------- INDEX CREATION ----------------------------- -- IDN_OAUTH2_ACCESS_TOKEN -- CREATE INDEX IDX_TC diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql index 1cc9ebe6bbed..8e17353460bb 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql @@ -1444,6 +1444,16 @@ CREATE TABLE IF NOT EXISTS IDN_RULE_REFERENCES ( FOREIGN KEY (RULE_ID) REFERENCES IDN_RULE(ID) ON DELETE CASCADE )DEFAULT CHARACTER SET latin1 ENGINE INNODB; +CREATE TABLE IF NOT EXISTS APP_GROUP_ASSOCIATION ( + ID INTEGER NOT NULL AUTO_INCREMENT, + APP_ID CHAR (36) NOT NULL, + GROUP_ID VARCHAR (255) NOT NULL, + UM_DOMAIN_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, + CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, UM_DOMAIN_ID) +)DEFAULT CHARACTER SET latin1 ENGINE INNODB; + -- --------------------------- INDEX CREATION ----------------------------- -- IDN_OAUTH2_ACCESS_TOKEN -- CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED); diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle.sql index a00c4143c7e2..295da3533ff2 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle.sql @@ -2227,6 +2227,27 @@ CREATE OR REPLACE TRIGGER IDN_RULE_REFERENCES_TRIGGER END; / +CREATE TABLE APP_GROUP_ASSOCIATION ( + ID INTEGER NOT NULL, + APP_ID CHAR (36) NOT NULL, + GROUP_ID VARCHAR (255) NOT NULL, + UM_DOMAIN_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, + CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, UM_DOMAIN_ID)) +/ +CREATE SEQUENCE APP_GROUP_ASSOCIATION_SEQ START WITH 1 INCREMENT BY 1 NOCACHE +/ +CREATE OR REPLACE TRIGGER APP_GROUP_ASSOCIATION_TRIG + BEFORE INSERT + ON APP_GROUP_ASSOCIATION + REFERENCING NEW AS NEW + FOR EACH ROW + BEGIN + SELECT APP_GROUP_ASSOCIATION_SEQ.nextval INTO :NEW.ID FROM dual; + END; +/ + -- --------------------------- INDEX CREATION ----------------------------- -- IDN_OAUTH2_ACCESS_TOKEN -- CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED) diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle_rac.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle_rac.sql index 57df16ec42e1..86640e906d94 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle_rac.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle_rac.sql @@ -2159,6 +2159,26 @@ CREATE OR REPLACE TRIGGER IDN_RULE_REFERENCES_TRIGGER SELECT IDN_RULE_REFERENCES_SEQ.nextval INTO :NEW.ID FROM dual; END; / +CREATE TABLE APP_GROUP_ASSOCIATION ( + ID INTEGER NOT NULL, + APP_ID CHAR (36) NOT NULL, + GROUP_ID VARCHAR (255) NOT NULL, + UM_DOMAIN_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, + CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, UM_DOMAIN_ID)) +/ +CREATE SEQUENCE APP_GROUP_ASSOCIATION_SEQ START WITH 1 INCREMENT BY 1 CACHE 20 +/ +CREATE OR REPLACE TRIGGER APP_GROUP_ASSOCIATION_TRIG + BEFORE INSERT + ON APP_GROUP_ASSOCIATION + REFERENCING NEW AS NEW + FOR EACH ROW + BEGIN + SELECT APP_GROUP_ASSOCIATION_SEQ.nextval INTO :NEW.ID FROM dual; + END; +/ -- --------------------------- INDEX CREATION ----------------------------- -- IDN_OAUTH2_ACCESS_TOKEN -- CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED) diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/postgresql.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/postgresql.sql index 86d57247fe3f..338f3fec2fb3 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/postgresql.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/postgresql.sql @@ -1691,6 +1691,19 @@ CREATE TABLE IDN_RULE_REFERENCES ( FOREIGN KEY (RULE_ID) REFERENCES IDN_RULE(ID) ON DELETE CASCADE ); +DROP TABLE IF EXISTS APP_GROUP_ASSOCIATION; +DROP SEQUENCE IF EXISTS APP_GROUP_ASSOCIATION_SEQ; +CREATE SEQUENCE APP_GROUP_ASSOCIATION_SEQ; +CREATE TABLE APP_GROUP_ASSOCIATION ( + ID INTEGER DEFAULT NEXTVAL('APP_GROUP_ASSOCIATION_SEQ'), + APP_ID CHAR(36) NOT NULL, + GROUP_ID VARCHAR (255) NOT NULL, + UM_DOMAIN_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, + CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, UM_DOMAIN_ID) +); + -- --------------------------- INDEX CREATION ----------------------------- -- IDN_OAUTH2_ACCESS_TOKEN -- CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED); From 18ae74deb1a1be06f36e4dfe6af09106ec7e0ea6 Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:28:03 +0530 Subject: [PATCH 03/23] implement functionality to retrieve and update discoverable groups --- .../common/model/DiscoverableGroup.java | 33 +++-- .../common/model/GroupBasicInfo.java | 117 +++++++++++++++ .../common/model/ServiceProvider.java | 5 +- .../test/model/DiscoverableGroupTest.java | 134 +++++++++--------- .../model/test/model/GroupBasicInfoTest.java | 110 ++++++++++++++ .../model/test/model/ServiceProviderTest.java | 74 ++++++++-- .../src/test/resources/testng.xml | 1 + .../pom.xml | 6 + .../application/mgt/ApplicationConstants.java | 10 +- .../application/mgt/ApplicationMgtUtil.java | 39 +++++ .../mgt/dao/impl/ApplicationDAOImpl.java | 129 +++++++++++++++++ .../mgt/dao/impl/ApplicationMgtDBQueries.java | 8 ++ ...ApplicationManagementServiceComponent.java | 20 +++ ...ationManagementServiceComponentHolder.java | 23 +++ .../DefaultApplicationValidator.java | 119 +++++++++++++++- .../resources/dbscripts/db2.sql | 8 +- .../resources/dbscripts/h2.sql | 8 +- .../resources/dbscripts/mssql.sql | 8 +- .../resources/dbscripts/mysql-cluster.sql | 8 +- .../resources/dbscripts/mysql.sql | 8 +- .../resources/dbscripts/oracle.sql | 8 +- .../resources/dbscripts/oracle_rac.sql | 8 +- .../resources/dbscripts/postgresql.sql | 8 +- 23 files changed, 765 insertions(+), 127 deletions(-) create mode 100644 components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/GroupBasicInfo.java create mode 100644 components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/GroupBasicInfoTest.java diff --git a/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/DiscoverableGroup.java b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/DiscoverableGroup.java index 84e423e975ed..82b52d1a8735 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/DiscoverableGroup.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/DiscoverableGroup.java @@ -29,6 +29,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; /** @@ -38,14 +39,17 @@ @XmlRootElement(name = "DiscoverableGroup") public class DiscoverableGroup implements Serializable { + private static final long serialVersionUID = -3030000000000000000L; private static final String USER_STORE = "UserStore"; + private static final String GROUPS = "Groups"; private static final String GROUP = "Group"; @XmlElement(name = USER_STORE) private String userStore; + @XmlElementWrapper(name = GROUPS) @XmlElement(name = GROUP) - private String[] groups; + private GroupBasicInfo[] groups; /** * Creates an instance of the DiscoverableGroup class by parsing an OMElement. @@ -57,7 +61,7 @@ public static DiscoverableGroup build(OMElement discoverableGroupOM) { DiscoverableGroup discoverableGroup = new DiscoverableGroup(); Iterator iter = discoverableGroupOM.getChildElements(); - List groupList = new ArrayList<>(); + List groupList = new ArrayList<>(); while (iter.hasNext()) { OMElement element = (OMElement) iter.next(); @@ -65,8 +69,15 @@ public static DiscoverableGroup build(OMElement discoverableGroupOM) { if (USER_STORE.equals(elementName) && StringUtils.isNotBlank(element.getText())) { discoverableGroup.setUserStore(element.getText()); - } else if (GROUP.equals(elementName) && StringUtils.isNotBlank(element.getText())) { - groupList.add(element.getText()); + } else if (GROUPS.equals(elementName)) { + Iterator groupIter = element.getChildElements(); + while (groupIter.hasNext()) { + OMElement groupElement = (OMElement) groupIter.next(); + GroupBasicInfo groupBasicInfo = GroupBasicInfo.build(groupElement); + if (groupBasicInfo != null) { + groupList.add(groupBasicInfo); + } + } } } @@ -74,26 +85,26 @@ public static DiscoverableGroup build(OMElement discoverableGroupOM) { return null; } - discoverableGroup.setGroups(groupList.toArray(new String[0])); + discoverableGroup.setGroups(groupList.toArray(new GroupBasicInfo[0])); return discoverableGroup; } /** - * Get the list of discoverable group IDs from the current user store. + * Get the list of discoverable group basic info for the current user store. * - * @return The list of group IDs. + * @return The list of group basic info. */ - public String[] getGroups() { + public GroupBasicInfo[] getGroups() { return groups; } /** - * Set the list of discoverable group IDs for the current user store. + * Set the list of discoverable group basic info for the current user store. * - * @param groups The list of group IDs. + * @param groups The list of group basic info. */ - public void setGroups(String[] groups) { + public void setGroups(GroupBasicInfo[] groups) { this.groups = groups; } diff --git a/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/GroupBasicInfo.java b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/GroupBasicInfo.java new file mode 100644 index 000000000000..27b02572e87a --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/GroupBasicInfo.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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.wso2.carbon.identity.application.common.model; + +import org.apache.axiom.om.OMElement; +import org.apache.commons.lang.StringUtils; + +import java.io.Serializable; +import java.util.Iterator; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Basic details of a group for discoverable group list. + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlRootElement(name = "Group") +public class GroupBasicInfo implements Serializable { + + private static final long serialVersionUID = -3030000000000000001L; + private static final String ID = "ID"; + private static final String NAME = "Name"; + + @XmlElement(name = ID) + private String id; + + @XmlElement(name = NAME) + private String name; + + /** + * Creates an instance of the GroupBasicInfo class by parsing an OMElement. + * + * @param groupOM The OMElement used to parse and build the GroupBasicInfo object. + * @return A new GroupBasicInfo object populated with data from the OMElement. + */ + public static GroupBasicInfo build(OMElement groupOM) { + + GroupBasicInfo groupBasicInfo = new GroupBasicInfo(); + Iterator iter = groupOM.getChildElements(); + + while (iter.hasNext()) { + OMElement element = (OMElement) iter.next(); + String elementName = element.getLocalName(); + + if (ID.equals(elementName) && StringUtils.isNotBlank(element.getText())) { + groupBasicInfo.setId(element.getText()); + } else if (NAME.equals(elementName) && StringUtils.isNotBlank(element.getText())) { + groupBasicInfo.setName(element.getText()); + } + } + + if (groupBasicInfo.getId() == null) { + return null; + } + + return groupBasicInfo; + } + + /** + * Get the group ID. + * + * @return Group ID. + */ + public String getId() { + + return id; + } + + /** + * Set the group ID. + * + * @param id Group ID. + */ + public void setId(String id) { + + this.id = id; + } + + /** + * Get the group name. + * + * @return Group name. + */ + public String getName() { + + return name; + } + + /** + * Set the group name. + * + * @param name Group name. + */ + public void setName(String name) { + + this.name = name; + } +} diff --git a/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/ServiceProvider.java b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/ServiceProvider.java index 39491a7786da..604e652c86e3 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/ServiceProvider.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/ServiceProvider.java @@ -317,7 +317,10 @@ public static ServiceProvider build(OMElement serviceProviderOM) { while (discoverableGroupIter.hasNext()) { OMElement discoverableGroupElement = (OMElement) discoverableGroupIter.next(); - discoverableGroupList.add(DiscoverableGroup.build(discoverableGroupElement)); + DiscoverableGroup discoverableGroup = DiscoverableGroup.build(discoverableGroupElement); + if (discoverableGroup != null) { + discoverableGroupList.add(discoverableGroup); + } } if (!discoverableGroupList.isEmpty()) { diff --git a/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/DiscoverableGroupTest.java b/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/DiscoverableGroupTest.java index 68793bbb1409..2eaa65e6a014 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/DiscoverableGroupTest.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/DiscoverableGroupTest.java @@ -22,6 +22,7 @@ import org.apache.axiom.om.OMXMLBuilderFactory; import org.testng.annotations.Test; import org.wso2.carbon.identity.application.common.model.DiscoverableGroup; +import org.wso2.carbon.identity.application.common.model.GroupBasicInfo; import java.io.StringReader; @@ -39,9 +40,20 @@ public void testBuildDiscoverableGroupInstanceFromXML() { final String discoverableGroupsXML = "\n" + " test-domain\n" + - " test-group-id-1\n" + - " test-group-id-2\n" + - " test-group-id-3\n" + + " \n" + + " \n" + + " test-group-id-1\n" + + " test-group-name-1\n" + + " \n" + + " \n" + + " test-group-id-2\n" + + " test-group-name-2\n" + + " \n" + + " \n" + + " test-group-id-3\n" + + " test-group-name-3\n" + + " \n" + + " \n" + ""; OMElement rootElement = @@ -51,12 +63,24 @@ public void testBuildDiscoverableGroupInstanceFromXML() { "The user store name does not match the user store name specified in the provided XML content"); assertEquals(discoverableGroup.getGroups().length, 3, "The group list does not match the group list specified in the provided XML content"); - assertEquals(discoverableGroup.getGroups()[0], "test-group-id-1", - "The group id does not match the group id specified in the provided XML content"); - assertEquals(discoverableGroup.getGroups()[1], "test-group-id-2", - "The group id does not match the group id specified in the provided XML content"); - assertEquals(discoverableGroup.getGroups()[2], "test-group-id-3", + assertGroup(discoverableGroup.getGroups()[0], "test-group-id-1", "test-group-name-1"); + assertGroup(discoverableGroup.getGroups()[1], "test-group-id-2", "test-group-name-2"); + assertGroup(discoverableGroup.getGroups()[2], "test-group-id-3", "test-group-name-3"); + } + + /** + * Assert the group basic info. + * + * @param groupBasicInfo GroupBasicInfo object for assertion. + * @param id Expected group id. + * @param name Expected group name. + */ + private void assertGroup(GroupBasicInfo groupBasicInfo, String id, String name) { + + assertEquals(groupBasicInfo.getId(), id, "The group id does not match the group id specified in the provided XML content"); + assertEquals(groupBasicInfo.getName(), name, + "The group name does not match the group name specified in the provided XML content"); } @Test(description = "Test the creation of the DiscoverableGroup model using XML content with empty user store") @@ -64,9 +88,20 @@ public void testBuildDiscoverableGroupInstanceFromXMLWithEmptyUserStore() { final String discoverableGroupsXML = "\n" + " \n" + - " test-group-id-1\n" + - " test-group-id-2\n" + - " test-group-id-3\n" + + " \n" + + " \n" + + " test-group-id-1\n" + + " test-group-name-1\n" + + " \n" + + " \n" + + " test-group-id-2\n" + + " test-group-name-2\n" + + " \n" + + " \n" + + " test-group-id-3\n" + + " test-group-name-3\n" + + " \n" + + " \n" + ""; OMElement rootElement = @@ -81,9 +116,20 @@ public void testBuildDiscoverableGroupInstanceFromXMLWithWhitespaceUserStore() { final String discoverableGroupsXML = "\n" + " \n" + - " test-group-id-1\n" + - " test-group-id-2\n" + - " test-group-id-3\n" + + " \n" + + " \n" + + " test-group-id-1\n" + + " test-group-name-1\n" + + " \n" + + " \n" + + " test-group-id-2\n" + + " test-group-name-2\n" + + " \n" + + " \n" + + " test-group-id-3\n" + + " test-group-name-3\n" + + " \n" + + " \n" + ""; OMElement rootElement = @@ -97,54 +143,9 @@ public void testBuildDiscoverableGroupInstanceFromXMLWithEmptyGroup() { final String discoverableGroupsXML = "\n" + " test-domain\n" + - " test-group-id-1\n" + - " \n" + - " test-group-id-3\n" + - ""; - - OMElement rootElement = - OMXMLBuilderFactory.createOMBuilder(new StringReader(discoverableGroupsXML)).getDocumentElement(); - DiscoverableGroup discoverableGroup = DiscoverableGroup.build(rootElement); - assertEquals(discoverableGroup.getUserStore(), "test-domain", - "The user store name does not match the user store name specified in the provided XML content"); - assertEquals(discoverableGroup.getGroups().length, 2, - "The group list does not match the valid group list specified in the provided XML content"); - assertEquals(discoverableGroup.getGroups()[0], "test-group-id-1", - "The group id does not match the group id specified in the provided XML content"); - assertEquals(discoverableGroup.getGroups()[1], "test-group-id-3", - "The group id does not match the group id specified in the provided XML content"); - } - - @Test(description = "Test the creation of the DiscoverableGroup model using XML content with whitespace group") - public void testBuildDiscoverableGroupInstanceFromXMLWithWhitespaceGroup() { - - final String discoverableGroupsXML = "\n" + - " test-domain\n" + - " test-group-id-1\n" + - " \n" + - " test-group-id-3\n" + - ""; - - OMElement rootElement = - OMXMLBuilderFactory.createOMBuilder(new StringReader(discoverableGroupsXML)).getDocumentElement(); - DiscoverableGroup discoverableGroup = DiscoverableGroup.build(rootElement); - assertEquals(discoverableGroup.getUserStore(), "test-domain", - "The user store name does not match the user store name specified in the provided XML content"); - assertEquals(discoverableGroup.getGroups().length, 2, - "The group list does not match the valid group list specified in the provided XML content"); - assertEquals(discoverableGroup.getGroups()[0], "test-group-id-1", - "The group id does not match the group id specified in the provided XML content"); - assertEquals(discoverableGroup.getGroups()[1], "test-group-id-3", - "The group id does not match the group id specified in the provided XML content"); - } - - @Test(description = "Test the creation of the DiscoverableGroup model using XML content without user store") - public void testBuildDiscoverableGroupInstanceFromXMLWithoutUserStore() { - - final String discoverableGroupsXML = "\n" + - " test-group-id-1\n" + - " test-group-id-2\n" + - " test-group-id-3\n" + + " \n" + + " \n" + + " \n" + ""; OMElement rootElement = @@ -153,12 +154,11 @@ public void testBuildDiscoverableGroupInstanceFromXMLWithoutUserStore() { assertNull(discoverableGroup); } - @Test(description = "Test the creation of the DiscoverableGroup model using XML content without groups") - public void testBuildDiscoverableGroupInstanceFromXMLWithoutGroups() { + @Test(description = "Test the creation of the DiscoverableGroup model using XML content without user store " + + "and groups") + public void testBuildDiscoverableGroupInstanceFromXMLWithoutUserStoreAndGroups() { - final String discoverableGroupsXML = "\n" + - " test-domain\n" + - ""; + final String discoverableGroupsXML = ""; OMElement rootElement = OMXMLBuilderFactory.createOMBuilder(new StringReader(discoverableGroupsXML)).getDocumentElement(); diff --git a/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/GroupBasicInfoTest.java b/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/GroupBasicInfoTest.java new file mode 100644 index 000000000000..4dedf42fca71 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/GroupBasicInfoTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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.wso2.carbon.identity.application.common.model.test.model; + +import org.apache.axiom.om.OMElement; +import org.apache.axiom.om.OMXMLBuilderFactory; +import org.testng.annotations.Test; +import org.wso2.carbon.identity.application.common.model.GroupBasicInfo; + +import java.io.StringReader; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +/** + * Unit tests for the GroupBasicInfo. + */ +@Test +public class GroupBasicInfoTest { + + @Test(description = "Test the creation of the GroupBasicInfo model using XML content") + public void testBuildGroupBasicInfoInstanceFromXML() { + + final String groupBasicInfoXML = "\n" + + " test-group-id\n" + + " test-group-name\n" + + ""; + + OMElement rootElement = + OMXMLBuilderFactory.createOMBuilder(new StringReader(groupBasicInfoXML)).getDocumentElement(); + GroupBasicInfo groupBasicInfo = GroupBasicInfo.build(rootElement); + + assertEquals(groupBasicInfo.getId(), "test-group-id", + "The group id does not match the group id specified in the provided XML content"); + assertEquals(groupBasicInfo.getName(), "test-group-name", + "The group name does not match the group name specified in the provided XML content"); + } + + @Test(description = "Test the creation of the GroupBasicInfo model using XML content without group name") + public void testBuildGroupBasicInfoInstanceFromXMLWithoutGroupName() { + + final String groupBasicInfoXML = "\n" + + " test-group-id\n" + + ""; + + OMElement rootElement = + OMXMLBuilderFactory.createOMBuilder(new StringReader(groupBasicInfoXML)).getDocumentElement(); + GroupBasicInfo groupBasicInfo = GroupBasicInfo.build(rootElement); + assertEquals(groupBasicInfo.getId(), "test-group-id", + "The group id does not match the group id specified in the provided XML content"); + assertNull(groupBasicInfo.getName()); + } + + @Test(description = "Test the creation of the GroupBasicInfo model using XML content without group id") + public void testBuildGroupBasicInfoInstanceFromXMLWithoutGroupID() { + + final String groupBasicInfoXML = "\n" + + " test-group-name\n" + + ""; + + OMElement rootElement = + OMXMLBuilderFactory.createOMBuilder(new StringReader(groupBasicInfoXML)).getDocumentElement(); + GroupBasicInfo groupBasicInfo = GroupBasicInfo.build(rootElement); + assertNull(groupBasicInfo); + } + + @Test(description = "Test the creation of the GroupBasicInfo model using XML content with empty group id") + public void testBuildGroupBasicInfoInstanceFromXMLWithEmptyGroupID() { + + final String groupBasicInfoXML = "\n" + + " \n" + + " test-group-name\n" + + ""; + + OMElement rootElement = + OMXMLBuilderFactory.createOMBuilder(new StringReader(groupBasicInfoXML)).getDocumentElement(); + GroupBasicInfo groupBasicInfo = GroupBasicInfo.build(rootElement); + assertNull(groupBasicInfo); + } + + @Test(description = "Test the creation of the GroupBasicInfo model using XML content with whitespace group id") + public void testBuildGroupBasicInfoInstanceFromXMLWithWhitespaceGroupID() { + + final String groupBasicInfoXML = "\n" + + " \n" + + " test-group-name\n" + + ""; + + OMElement rootElement = + OMXMLBuilderFactory.createOMBuilder(new StringReader(groupBasicInfoXML)).getDocumentElement(); + GroupBasicInfo groupBasicInfo = GroupBasicInfo.build(rootElement); + assertNull(groupBasicInfo); + } +} diff --git a/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/ServiceProviderTest.java b/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/ServiceProviderTest.java index e9ee9f6614f6..02631ff1abbe 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/ServiceProviderTest.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/java/org/wso2/carbon/identity/application/common/model/test/model/ServiceProviderTest.java @@ -22,6 +22,7 @@ import org.apache.axiom.om.OMXMLBuilderFactory; import org.testng.annotations.Test; import org.wso2.carbon.identity.application.common.model.DiscoverableGroup; +import org.wso2.carbon.identity.application.common.model.GroupBasicInfo; import org.wso2.carbon.identity.application.common.model.ServiceProvider; import java.io.StringReader; @@ -44,13 +45,29 @@ public void testDiscoverableGroupsList() { " \n" + " \n" + " test-domain-1\n" + - " test-id-1\n" + - " test-id-2\n" + + " \n" + + " \n" + + " test-id-1\n" + + " test-name-1\n" + + " \n" + + " \n" + + " test-id-2\n" + + " test-name-2\n" + + " \n" + + " \n" + " \n" + " \n" + " test-domain-2\n" + - " test-id-3\n" + - " test-id-4\n" + + " \n" + + " \n" + + " test-id-3\n" + + " test-name-3\n" + + " \n" + + " \n" + + " test-id-4\n" + + " test-name-4\n" + + " \n" + + " \n" + " \n" + " \n" + ""; @@ -63,20 +80,55 @@ public void testDiscoverableGroupsList() { assertEquals(discoverableGroups.length, 2, "The discoverable group list does not match the discoverable group list specified in the provided" + " XML content"); - assertEquals(discoverableGroups[0].getUserStore(), "test-domain-1", - "The user store name does not match the user store name specified in the provided XML content"); - assertEquals(discoverableGroups[0].getGroups(), new String[] {"test-id-1", "test-id-2"}, - "The group list does not match the group list specified in the provided XML content"); - assertEquals(discoverableGroups[1].getUserStore(), "test-domain-2", + assertDiscoverableGroup(discoverableGroups[0], "test-domain-1", new String[]{"test-id-1", "test-id-2"}, + new String[]{"test-name-1", "test-name-2"}); + assertDiscoverableGroup(discoverableGroups[1], "test-domain-2", new String[]{"test-id-3", "test-id-4"}, + new String[]{"test-name-3", "test-name-4"}); + } + + /** + * Assert the discoverable group. + * + * @param discoverableGroup Discoverable group instance for assertion. + * @param userStore Expected user store name. + * @param groupIDs Expected group IDs list. + * @param groupName Expected group names list. + */ + private void assertDiscoverableGroup(DiscoverableGroup discoverableGroup, String userStore, String[] groupIDs, + String[] groupName) { + + assertEquals(discoverableGroup.getUserStore(), userStore, "The user store name does not match the user store name specified in the provided XML content"); - assertEquals(discoverableGroups[1].getGroups(), new String[] {"test-id-3", "test-id-4"}, - "The group list does not match the group list specified in the provided XML content"); + GroupBasicInfo[] groups = discoverableGroup.getGroups(); + for (int i = 0; i < groups.length; i++) { + GroupBasicInfo groupBasicInfo = groups[i]; + assertEquals(groupBasicInfo.getId(), groupIDs[i], + "The group ID does not match the group ID specified in the provided XML content"); + assertEquals(groupBasicInfo.getName(), groupName[i], + "The group name does not match the group name specified in the provided XML content"); + } } @Test(description = "Test the construction of the empty DiscoverableGroups list from the service provider" + " as XML content") public void testEmptyDiscoverableGroupsList() { + final String serviceProviderXML = "\n" + + " TestApp\n" + + " \n" + + " \n" + + " \n" + + ""; + + OMElement rootElement = + OMXMLBuilderFactory.createOMBuilder(new StringReader(serviceProviderXML)).getDocumentElement(); + ServiceProvider serviceProvider = ServiceProvider.build(rootElement); + assertNull(serviceProvider.getDiscoverableGroups()); + } + + @Test(description = "Test the construction of the service provider without DiscoverableGroups from XML content") + public void testWithoutDiscoverableGroupsList() { + final String serviceProviderXML = "\n" + " TestApp\n" + ""; diff --git a/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/resources/testng.xml b/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/resources/testng.xml index 6db693e843c4..2f06e45b3757 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/resources/testng.xml +++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/resources/testng.xml @@ -26,6 +26,7 @@ + diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/pom.xml b/components/application-mgt/org.wso2.carbon.identity.application.mgt/pom.xml index 194fff38ac76..d15a95145be6 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/pom.xml +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/pom.xml @@ -195,6 +195,11 @@ org.wso2.carbon.identity.framework org.wso2.carbon.identity.certificate.management + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.user.store.configuration + provided + @@ -267,6 +272,7 @@ org.wso2.carbon.identity.certificate.management.service; version="${carbon.identity.package.import.version.range}", org.wso2.carbon.identity.certificate.management.exception; version="${carbon.identity.package.import.version.range}", org.wso2.carbon.identity.certificate.management.model; version="${carbon.identity.package.import.version.range}", + org.wso2.carbon.identity.user.store.configuration; version="${carbon.identity.package.import.version.range}", !org.wso2.carbon.identity.application.mgt.internal, diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java index b120ce2f3bf2..93c5be573ef2 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java @@ -262,10 +262,16 @@ public enum ErrorMessage { "Error occurred while retrieving user by userid: %s."), NON_EXISTING_USER_ID("60504", "User not found", "No user found for the given user-id: %s."), + UNSUPPORTED_USER_STORE_MANAGER("65003", "Unsupported user store manager.", + "The underlying user store manager is not a unique ID user store manager for the tenant: %s."), ERROR_RETRIEVING_USERSTORE_MANAGER("65504", "Error retrieving userstore manager.", - "Error occurred while retrieving userstore manager."), + "Error occurred while retrieving userstore manager for the tenant: %s."), UNEXPECTED_ERROR("65006", "Unexpected processing error.", - "Server encountered an unexpected error when creating the application."); + "Server encountered an unexpected error when creating the application."), + ERROR_CHECKING_GROUP_EXISTENCE("65007", "Unexpected processing error.", + "Error occurred while checking the existence of the group: %s."), + ERROR_CHECKING_USER_STORE_EXISTENCE("65008", "Unexpected processing error.", + "Error occurred while checking the existence of the user store: %s."); private final String code; diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtil.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtil.java index 75e581154ae1..de76c379277e 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtil.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtil.java @@ -57,11 +57,13 @@ import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException; +import org.wso2.carbon.identity.role.v2.mgt.core.exception.IdentityRoleManagementServerException; import org.wso2.carbon.user.api.Tenant; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.api.UserStoreManager; import org.wso2.carbon.user.core.UserCoreConstants; import org.wso2.carbon.user.core.common.AbstractUserStoreManager; +import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.user.core.util.UserCoreUtil; import org.wso2.carbon.utils.multitenancy.MultitenantUtils; @@ -82,6 +84,8 @@ import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.CONSOLE_ACCESS_ORIGIN; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.CONSOLE_ACCESS_URL_FROM_SERVER_CONFIGS; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ENABLE_APPLICATION_ROLE_VALIDATION_PROPERTY; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.ERROR_RETRIEVING_USERSTORE_MANAGER; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.UNSUPPORTED_USER_STORE_MANAGER; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.LogConstants.APP_OWNER; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.LogConstants.DISABLE_LEGACY_AUDIT_LOGS_IN_APP_MGT_CONFIG; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.LogConstants.ENABLE_V2_AUDIT_LOGS; @@ -1266,4 +1270,39 @@ private static String getInboundConfigType(ServiceProvider serviceProvider) { } return inboundConfigType; } + + /** + * Get the AbstractUserStoreManager for the given tenant domain. + * + * @param tenantDomain Tenant domain. + * @return UserStoreManager. + * @throws IdentityApplicationManagementException If an error occurred while getting the AbstractUserStoreManager + * instance of the tenant. + */ + public static AbstractUserStoreManager getUserStoreManager(String tenantDomain) + throws IdentityApplicationManagementException { + + RealmService realmService = ApplicationManagementServiceComponentHolder.getInstance().getRealmService(); + UserStoreManager userStoreManager; + try { + userStoreManager = + realmService.getTenantUserRealm(IdentityTenantUtil.getTenantId(tenantDomain)) + .getUserStoreManager(); + } catch (UserStoreException e) { + throw new IdentityApplicationManagementException(ERROR_RETRIEVING_USERSTORE_MANAGER.getCode(), + ERROR_RETRIEVING_USERSTORE_MANAGER.getMessage(), + String.format(ERROR_RETRIEVING_USERSTORE_MANAGER.getDescription(), tenantDomain), e); + } + if (userStoreManager == null) { + throw new IdentityApplicationManagementException(ERROR_RETRIEVING_USERSTORE_MANAGER.getCode(), + ERROR_RETRIEVING_USERSTORE_MANAGER.getMessage(), + String.format(ERROR_RETRIEVING_USERSTORE_MANAGER.getDescription(), tenantDomain)); + } + if (!(userStoreManager instanceof AbstractUserStoreManager)) { + throw new IdentityApplicationManagementException(UNSUPPORTED_USER_STORE_MANAGER.getCode(), + UNSUPPORTED_USER_STORE_MANAGER.getMessage(), + String.format(UNSUPPORTED_USER_STORE_MANAGER.getDescription(), tenantDomain)); + } + return (AbstractUserStoreManager) userStoreManager; + } } diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java index e89116b81d8e..f489f7e4dbc6 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java @@ -18,6 +18,7 @@ package org.wso2.carbon.identity.application.mgt.dao.impl; +import java.util.Locale; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.ArrayUtils; @@ -44,7 +45,9 @@ import org.wso2.carbon.identity.application.common.model.ConsentConfig; import org.wso2.carbon.identity.application.common.model.ConsentPurpose; import org.wso2.carbon.identity.application.common.model.ConsentPurposeConfigs; +import org.wso2.carbon.identity.application.common.model.DiscoverableGroup; import org.wso2.carbon.identity.application.common.model.FederatedAuthenticatorConfig; +import org.wso2.carbon.identity.application.common.model.GroupBasicInfo; import org.wso2.carbon.identity.application.common.model.IdentityProvider; import org.wso2.carbon.identity.application.common.model.InboundAuthenticationConfig; import org.wso2.carbon.identity.application.common.model.InboundAuthenticationRequestConfig; @@ -105,6 +108,7 @@ import org.wso2.carbon.identity.secret.mgt.core.model.Secret; import org.wso2.carbon.user.api.Tenant; import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.user.core.common.AbstractUserStoreManager; import org.wso2.carbon.user.core.util.UserCoreUtil; import org.wso2.carbon.utils.DBUtils; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; @@ -631,6 +635,7 @@ private void addApplicationConfigurations(Connection connection, ServiceProvider updateOutboundProvisioningConfiguration(applicationId, serviceProvider.getOutboundProvisioningConfig(), connection); updateSpTrustedAppMetadata(applicationId, serviceProvider.getTrustedAppMetadata(), connection, tenantID); + updateDiscoverableGroups(applicationId, serviceProvider.getDiscoverableGroups(), connection, tenantID); if (serviceProvider.getPermissionAndRoleConfig() != null) { updatePermissionAndRoleConfiguration(serviceProvider.getApplicationID(), @@ -679,6 +684,7 @@ private void deleteApplicationConfigurations(Connection connection, ServiceProvi // deleteConsentPurposeConfiguration(connection, applicationId, tenantID); deleteAssociatedRolesConfigurations(connection, serviceProvider.getApplicationResourceId()); deleteSpTrustedAppMetadata(applicationId, connection, tenantID); + deleteDiscoverableGroups(applicationId, connection); } private void deleteAssociatedRolesConfigurations(Connection connection, String applicationId) throws SQLException { @@ -2002,6 +2008,7 @@ private ServiceProvider getBasicApplicationData(String applicationName, Connecti serviceProvider.setDiscoverable(getBooleanValue(basicAppDataResultSet.getString(ApplicationTableColumns .IS_DISCOVERABLE))); + serviceProvider.setDiscoverableGroups(getDiscoverableGroups(serviceProvider.getApplicationID(), connection, tenantDomain)); User owner = new User(); owner.setUserName(basicAppDataResultSet.getString(5)); @@ -2233,6 +2240,8 @@ public ServiceProvider getApplication(int applicationId) throws IdentityApplicat if (serviceProvider == null) { return null; } + serviceProvider.setDiscoverableGroups( + getDiscoverableGroups(applicationId, connection, serviceProvider.getTenantDomain())); int tenantID = IdentityTenantUtil.getTenantId(serviceProvider.getTenantDomain()); List propertyList = getServicePropertiesBySpId(connection, applicationId); @@ -2674,6 +2683,7 @@ private ServiceProvider getBasicApplicationData(int appId, Connection connection } serviceProvider.setDiscoverable(getBooleanValue(rs.getString(ApplicationTableColumns.IS_DISCOVERABLE))); + serviceProvider.setDiscoverableGroups(getDiscoverableGroups(appId, connection, tenantDomain)); User owner = new User(); owner.setUserName(rs.getString(ApplicationTableColumns.USERNAME)); @@ -6738,4 +6748,123 @@ private void rollbackAddApplicationTransaction(Connection connection, ServicePro throw new IdentityApplicationManagementException("Error while rolling back the transaction.", e); } } + + /** + * Retrieve the discoverable groups for the application. + * + * @param applicationId Application ID. + * @param connection Database connection. + * @param tenantDomain Tenant domain of the application. + * @return Discoverable groups list. + * @throws IdentityApplicationManagementException If an error occurred while retrieving discoverable groups. + */ + private DiscoverableGroup[] getDiscoverableGroups(int applicationId, Connection connection, String tenantDomain) + throws IdentityApplicationManagementException { + + if (log.isDebugEnabled()) { + log.debug("Retrieving discoverable groups for application with ID: " + applicationId); + } + + Map> groupInfoMap = new HashMap<>(); + try (PreparedStatement statement = connection.prepareStatement( + ApplicationMgtDBQueries.GET_GROUP_ASSOCIATIONS_BY_APP_ID)) { + statement.setInt(1, applicationId); + try (ResultSet resultSet = statement.executeQuery()) { + AbstractUserStoreManager userStoreManager = + ApplicationMgtUtil.getUserStoreManager(tenantDomain); + while (resultSet.next()) { + String groupID = resultSet.getString(1); + String domainName = resultSet.getString(2); + GroupBasicInfo groupBasicInfo = new GroupBasicInfo(); + groupBasicInfo.setId(groupID); + try { + String groupName = userStoreManager.getGroupNameByGroupId( + UserCoreUtil.addDomainToName(groupID, domainName)); + groupBasicInfo.setName(UserCoreUtil.removeDomainFromName(groupName)); + if (groupInfoMap.containsKey(domainName)) { + groupInfoMap.get(domainName).add(groupBasicInfo); + } else { + List groupBasicInfoList = new ArrayList<>(); + groupBasicInfoList.add(groupBasicInfo); + groupInfoMap.put(domainName, groupBasicInfoList); + } + } catch (UserStoreException e) { + log.warn("Error while retrieving group name for group ID: " + groupID, e); + } + } + } + } catch (SQLException e) { + throw new IdentityApplicationManagementException( + "Error while retrieving discoverable groups for the application", e); + } + List discoverableGroups = new ArrayList<>(); + for (String domainName : groupInfoMap.keySet()) { + DiscoverableGroup discoverableGroup = new DiscoverableGroup(); + discoverableGroup.setUserStore(domainName); + discoverableGroup.setGroups(groupInfoMap.get(domainName).toArray(new GroupBasicInfo[0])); + discoverableGroups.add(discoverableGroup); + } + if (!discoverableGroups.isEmpty()) { + return discoverableGroups.toArray(new DiscoverableGroup[0]); + } + return null; + } + + /** + * Update the discoverable groups for the application. + * + * @param applicationId Application ID. + * @param discoverableGroups Discoverable groups. + * @param connection Database connection. + * @param tenantId Tenant ID. + * @throws IdentityApplicationManagementException If an error occurred while updating discoverable groups. + */ + private void updateDiscoverableGroups(int applicationId, DiscoverableGroup[] discoverableGroups, + Connection connection, int tenantId) + throws IdentityApplicationManagementException { + + if (log.isDebugEnabled()) { + log.debug("Adding discoverable groups for application with ID: " + applicationId); + } + + if (discoverableGroups == null) { + return; + } + + try (PreparedStatement statement = connection.prepareStatement( + ApplicationMgtDBQueries.ADD_APP_GROUP_ASSOCIATION)) { + for (DiscoverableGroup discoverableGroup : discoverableGroups) { + for (GroupBasicInfo groupBasicInfo : discoverableGroup.getGroups()) { + statement.setInt(1, applicationId); + statement.setString(2, groupBasicInfo.getId()); + statement.setString(3, discoverableGroup.getUserStore().toUpperCase(Locale.ENGLISH)); + statement.addBatch(); + } + } + statement.executeBatch(); + } catch (SQLException e) { + throw new IdentityApplicationManagementException("Error while adding discoverable groups for application", + e); + } + } + + /** + * Delete the discoverable groups for the application. + * + * @param applicationId Application ID. + * @param connection Database connection. + * @throws SQLException If an error occurred while deleting discoverable groups. + */ + private void deleteDiscoverableGroups(int applicationId, Connection connection) throws SQLException { + + if (log.isDebugEnabled()) { + log.debug("Deleting discoverable groups for application with ID: " + applicationId); + } + + try (PreparedStatement statement = connection.prepareStatement( + ApplicationMgtDBQueries.DELETE_APP_GROUP_ASSOCIATION_BY_APP_ID)) { + statement.setInt(1, applicationId); + statement.executeUpdate(); + } + } } diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationMgtDBQueries.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationMgtDBQueries.java index cee4d438c206..cef15cbea858 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationMgtDBQueries.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationMgtDBQueries.java @@ -578,6 +578,14 @@ public class ApplicationMgtDBQueries { public static final String ADD_APPLICATION_ASSOC_ROLE = "INSERT INTO APP_ROLE_ASSOCIATION " + "(APP_ID, ROLE_ID) VALUES (?, ?)"; + // App group association related database queries. + public static final String ADD_APP_GROUP_ASSOCIATION = "INSERT INTO APP_GROUP_ASSOCIATION (APP_ID, GROUP_ID, " + + "DOMAIN_NAME) VALUES (?, ?, ?)"; + public static final String DELETE_APP_GROUP_ASSOCIATION_BY_APP_ID = + "DELETE FROM APP_GROUP_ASSOCIATION WHERE APP_ID = ?"; + public static final String GET_GROUP_ASSOCIATIONS_BY_APP_ID = + "SELECT GROUP_ID, DOMAIN_NAME FROM APP_GROUP_ASSOCIATION WHERE APP_ID = ?"; + /** * SQL placeholders related to application management tables. */ diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java index 986b2374dad9..843640c705cf 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java @@ -74,10 +74,12 @@ import org.wso2.carbon.identity.organization.management.service.OrganizationManagementInitialize; import org.wso2.carbon.identity.organization.management.service.OrganizationManager; import org.wso2.carbon.identity.organization.management.service.OrganizationUserResidentResolverService; +import org.wso2.carbon.identity.role.v2.mgt.core.GroupManagementService; import org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService; import org.wso2.carbon.identity.role.v2.mgt.core.listener.RoleManagementListener; import org.wso2.carbon.identity.secret.mgt.core.SecretManager; import org.wso2.carbon.identity.secret.mgt.core.SecretResolveManager; +import org.wso2.carbon.identity.user.store.configuration.UserStoreConfigService; import org.wso2.carbon.idp.mgt.listener.IdentityProviderMgtListener; import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.utils.CarbonUtils; @@ -658,4 +660,22 @@ protected void unsetApplicationCertificateManagementService(ApplicationCertifica ApplicationManagementServiceComponentHolder.getInstance().setApplicationCertificateMgtService(null); log.debug("ApplicationCertificateManagementService unset in ApplicationManagementServiceComponent bundle."); } + + @Reference( + name = "org.wso2.carbon.identity.user.store.configuration.UserStoreConfigService", + service = org.wso2.carbon.identity.user.store.configuration.UserStoreConfigService.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetUserStoreConfigService") + protected void setUserStoreConfigService(UserStoreConfigService userStoreConfigService) { + + ApplicationManagementServiceComponentHolder.getInstance().setUserStoreConfigService(userStoreConfigService); + log.debug("UserStoreConfigService set in ApplicationManagementServiceComponent bundle."); + } + + protected void unsetUserStoreConfigService(UserStoreConfigService userStoreConfigService) { + + ApplicationManagementServiceComponentHolder.getInstance().setUserStoreConfigService(null); + log.debug("UserStoreConfigService unset in ApplicationManagementServiceComponent bundle."); + } } diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java index 289cde9c84f8..a328bd4e869a 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java @@ -29,9 +29,11 @@ import org.wso2.carbon.identity.organization.management.service.OrganizationManagementInitialize; import org.wso2.carbon.identity.organization.management.service.OrganizationManager; import org.wso2.carbon.identity.organization.management.service.OrganizationUserResidentResolverService; +import org.wso2.carbon.identity.role.v2.mgt.core.GroupManagementService; import org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService; import org.wso2.carbon.identity.secret.mgt.core.SecretManager; import org.wso2.carbon.identity.secret.mgt.core.SecretResolveManager; +import org.wso2.carbon.identity.user.store.configuration.UserStoreConfigService; import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.utils.ConfigurationContextService; @@ -69,6 +71,7 @@ public class ApplicationManagementServiceComponentHolder { private ApplicationPermissionProvider applicationPermissionProvider; private APIResourceManager apiResourceManager; private RoleManagementService roleManagementServiceV2; + private UserStoreConfigService userStoreConfigService; private OrganizationManager organizationManager; private boolean isOrganizationManagementEnable = false; @@ -440,4 +443,24 @@ public void setApplicationCertificateMgtService( this.applicationCertificateMgtService = applicationCertificateMgtService; } + + /** + * Get UserStoreConfigService instance. + * + * @return UserStoreConfigService instance. + */ + public UserStoreConfigService getUserStoreConfigService() { + + return userStoreConfigService; + } + + /** + * Set UserStoreConfigService instance. + * + * @param userStoreConfigService UserStoreConfigService instance. + */ + public void setUserStoreConfigService(UserStoreConfigService userStoreConfigService) { + + this.userStoreConfigService = userStoreConfigService; + } } diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java index 7c89df0d7671..b0aa48bb1c97 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java @@ -19,6 +19,7 @@ package org.wso2.carbon.identity.application.mgt.validator; +import java.util.Objects; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; @@ -29,7 +30,9 @@ import org.wso2.carbon.identity.application.common.model.AuthenticationStep; import org.wso2.carbon.identity.application.common.model.ClaimConfig; import org.wso2.carbon.identity.application.common.model.ClaimMapping; +import org.wso2.carbon.identity.application.common.model.DiscoverableGroup; import org.wso2.carbon.identity.application.common.model.FederatedAuthenticatorConfig; +import org.wso2.carbon.identity.application.common.model.GroupBasicInfo; import org.wso2.carbon.identity.application.common.model.IdentityProvider; import org.wso2.carbon.identity.application.common.model.InboundAuthenticationConfig; import org.wso2.carbon.identity.application.common.model.InboundAuthenticationRequestConfig; @@ -50,10 +53,14 @@ import org.wso2.carbon.identity.application.mgt.ApplicationMgtUtil; import org.wso2.carbon.identity.application.mgt.dao.ApplicationDAO; import org.wso2.carbon.identity.application.mgt.dao.impl.ApplicationDAOImpl; +import org.wso2.carbon.identity.application.mgt.internal.ApplicationManagementServiceComponentHolder; import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataManagementServiceImpl; import org.wso2.carbon.identity.claim.metadata.mgt.exception.ClaimMetadataException; import org.wso2.carbon.identity.claim.metadata.mgt.model.ClaimDialect; import org.wso2.carbon.identity.core.util.IdentityUtil; +import org.wso2.carbon.identity.user.store.configuration.dto.UserStoreDTO; +import org.wso2.carbon.identity.user.store.configuration.utils.IdentityUserStoreClientException; +import org.wso2.carbon.identity.user.store.configuration.utils.IdentityUserStoreMgtException; import org.wso2.carbon.idp.mgt.IdentityProviderManagementException; import org.wso2.carbon.idp.mgt.IdentityProviderManager; import org.wso2.carbon.user.api.UserStoreException; @@ -68,7 +75,12 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.wso2.carbon.user.core.UserStoreClientException; +import org.wso2.carbon.user.core.common.AbstractUserStoreManager; +import org.wso2.carbon.user.core.util.UserCoreUtil; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.ERROR_CHECKING_GROUP_EXISTENCE; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.ERROR_CHECKING_USER_STORE_EXISTENCE; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.TRUSTED_APP_MAX_THUMBPRINT_COUNT_PROPERTY; import static org.wso2.carbon.user.core.UserCoreConstants.INTERNAL_DOMAIN; import static org.wso2.carbon.user.core.UserCoreConstants.WORKFLOW_DOMAIN; @@ -104,6 +116,20 @@ public class DefaultApplicationValidator implements ApplicationValidator { "if FIDO trusted app feature is enabled."; private static final String INCORRECT_TRUSTED_ANDROID_APP_DETAILS = "Both package name and thumbprints are " + "required when configuring an android application as a trusted mobile application."; + private static final String DISCOVERABLE_GROUPS_FOR_NON_DISCOVERABLE_APPLICATION = + "Discoverable groups are defined for a non-discoverable application."; + private static final String EMPTY_DISCOVERABLE_GROUPS = "The list of discoverable groups is empty."; + private static final String NO_USER_STORE_FOR_THE_DISCOVERABLE_GROUP = + "No user store defined for the discoverable groups indexed at %d."; + private static final String USER_STORE_NOT_FOUND = + "The provided user store is not found for the discoverable groups indexed at %d."; + private static final String NO_GROUPS_FOR_THE_DISCOVERABLE_GROUP = + "No groups defined for the user store: '%s' in the discoverable groups configuration."; + private static final String NO_GROUP_ID = "Group ID is not defined for the group indexed at %d for the user " + + "store: '%s' in the discoverable groups configuration."; + private static final String GROUP_NAME_NOT_MATCH_WITH_GROUP_ID = "Group name '%s' does not match with the user " + + "store group name for the group ID '%s' in the discoverable groups configuration."; + private static final String NO_GROUP_WITH_GIVEN_ID = "No group found for the given group ID: '%s'."; public static final String IS_HANDLER = "IS_HANDLER"; private static Pattern loopPattern; private static final int MODE_DEFAULT = 1; @@ -130,7 +156,7 @@ public List validateApplication(ServiceProvider serviceProvider, String List validationErrors = new ArrayList<>(); validateApplicationVersion(validationErrors, serviceProvider); - validateDiscoverabilityConfigs(validationErrors, serviceProvider); + validateDiscoverabilityConfigs(validationErrors, serviceProvider, tenantDomain); validateInboundAuthenticationConfig(serviceProvider.getInboundAuthenticationConfig(), tenantDomain, serviceProvider.getApplicationID()); validateLocalAndOutBoundAuthenticationConfig(validationErrors, @@ -169,8 +195,16 @@ private void validateApplicationVersion(List validationErrors, ServicePr } } - private void validateDiscoverabilityConfigs(List validationErrors, - ServiceProvider serviceProvider) { + /** + * Validate whether the discoverability configurations are valid. + * + * @param validationErrors List of validation errors. + * @param serviceProvider Service provider configuration. + * @param tenantDomain Tenant domain of the application. + * @throws IdentityApplicationManagementException If an error occurs while validating the discoverability configurations. + */ + private void validateDiscoverabilityConfigs(List validationErrors, ServiceProvider serviceProvider, + String tenantDomain) throws IdentityApplicationManagementException { String validationErrorFormat = "A valid %s needs to be defined if an application is marked as discoverable."; if (serviceProvider.isDiscoverable()) { @@ -178,6 +212,85 @@ private void validateDiscoverabilityConfigs(List validationErrors, validationErrors.add(String.format(validationErrorFormat, "accessURL")); } } + validateDiscoverableGroups(validationErrors, serviceProvider, tenantDomain); + } + + /** + * Validate whether the provided discoverable groups are valid. + * + * @param validationErrors List of validation errors. + * @param serviceProvider Service provider configuration. + * @param tenantDomain Tenant domain of the application. + * @throws IdentityApplicationManagementException If an error occurs while validating the discoverable groups. + */ + private void validateDiscoverableGroups(List validationErrors, ServiceProvider serviceProvider, + String tenantDomain) throws IdentityApplicationManagementException { + + DiscoverableGroup[] discoverableGroups = serviceProvider.getDiscoverableGroups(); + if (discoverableGroups != null) { + if (!serviceProvider.isDiscoverable()) { + validationErrors.add(DISCOVERABLE_GROUPS_FOR_NON_DISCOVERABLE_APPLICATION); + return; + } + if (discoverableGroups.length == 0) { + validationErrors.add(EMPTY_DISCOVERABLE_GROUPS); + return; + } + for (int i = 0; i < discoverableGroups.length; i++) { + DiscoverableGroup discoverableGroup = discoverableGroups[i]; + GroupBasicInfo[] groupBasicInfos = discoverableGroup.getGroups(); + if (discoverableGroup.getUserStore() == null) { + validationErrors.add(String.format(NO_USER_STORE_FOR_THE_DISCOVERABLE_GROUP, i)); + continue; + } + try { + ApplicationManagementServiceComponentHolder.getInstance().getUserStoreConfigService() + .getUserStore(discoverableGroup.getUserStore()); + } catch (IdentityUserStoreMgtException e) { + if (e instanceof IdentityUserStoreClientException) { + validationErrors.add(String.format(USER_STORE_NOT_FOUND, i)); + continue; + } + throw new IdentityApplicationManagementException(ERROR_CHECKING_USER_STORE_EXISTENCE.getCode(), + ERROR_CHECKING_USER_STORE_EXISTENCE.getMessage(), + String.format(ERROR_CHECKING_USER_STORE_EXISTENCE.getDescription(), + discoverableGroup.getUserStore()), e); + } + if (groupBasicInfos == null || groupBasicInfos.length == 0) { + validationErrors.add( + String.format(NO_GROUPS_FOR_THE_DISCOVERABLE_GROUP, discoverableGroup.getUserStore())); + continue; + } + for (int j = 0; j < groupBasicInfos.length; j++) { + GroupBasicInfo groupBasicInfo = groupBasicInfos[j]; + if (groupBasicInfo.getId() == null) { + validationErrors.add(String.format(NO_GROUP_ID, j, discoverableGroup.getUserStore())); + continue; + } + AbstractUserStoreManager userStoreManager = + ApplicationMgtUtil.getUserStoreManager(tenantDomain); + try { + String groupName = userStoreManager.getGroupNameByGroupId( + UserCoreUtil.addDomainToName(groupBasicInfo.getId(), discoverableGroup.getUserStore())); + String groupNameWithoutDomain = UserCoreUtil.removeDomainFromName(groupName); + if (!Objects.equals(groupNameWithoutDomain, groupBasicInfo.getName()) && + !Objects.equals(groupName, groupBasicInfo.getName())) { + log.warn(String.format(GROUP_NAME_NOT_MATCH_WITH_GROUP_ID, groupBasicInfo.getName(), + groupBasicInfo.getId())); + } + } catch (UserStoreException e) { + if (e instanceof UserStoreClientException) { + validationErrors.add(String.format(NO_GROUP_WITH_GIVEN_ID, groupBasicInfo.getId())); + continue; + } + throw new IdentityApplicationManagementException(ERROR_CHECKING_GROUP_EXISTENCE.getCode(), + ERROR_CHECKING_GROUP_EXISTENCE.getMessage(), + String.format(ERROR_CHECKING_GROUP_EXISTENCE.getDescription(), groupBasicInfo.getId()), + e); + } + } + } + } } /** diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/db2.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/db2.sql index 28361001b2a9..8ca6f474cc95 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/db2.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/db2.sql @@ -2162,12 +2162,12 @@ REFERENCING NEW AS NEW FOR EACH ROW MODE DB2SQL CREATE TABLE APP_GROUP_ASSOCIATION ( ID INTEGER NOT NULL, - APP_ID CHAR(36) NOT NULL, + APP_ID INTEGER NOT NULL, GROUP_ID VARCHAR (255) NOT NULL, - UM_DOMAIN_ID INTEGER NOT NULL, + DOMAIN_NAME VARCHAR (255) NOT NULL, PRIMARY KEY (ID), - FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, - CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, UM_DOMAIN_ID)) + FOREIGN KEY (APP_ID) REFERENCES SP_APP(ID) ON DELETE CASCADE, + CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, DOMAIN_NAME)) / CREATE SEQUENCE APP_GROUP_ASSOCIATION_SEQ START WITH 1 INCREMENT BY 1 NOCACHE / diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql index d3652d8babab..5b764c7dbd2e 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/h2.sql @@ -1415,12 +1415,12 @@ CREATE TABLE IF NOT EXISTS IDN_RULE_REFERENCES ( CREATE TABLE IF NOT EXISTS APP_GROUP_ASSOCIATION ( ID INTEGER NOT NULL AUTO_INCREMENT, - APP_ID CHAR (36) NOT NULL, + APP_ID INTEGER NOT NULL, GROUP_ID VARCHAR (255) NOT NULL, - UM_DOMAIN_ID INTEGER NOT NULL, + DOMAIN_NAME VARCHAR (255) NOT NULL, PRIMARY KEY (ID), - FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, - CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, UM_DOMAIN_ID) + FOREIGN KEY (APP_ID) REFERENCES SP_APP(ID) ON DELETE CASCADE, + CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, DOMAIN_NAME) ); -- --------------------------- INDEX CREATION ----------------------------- diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mssql.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mssql.sql index 09b99e982c6f..32763a7bf017 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mssql.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mssql.sql @@ -1568,12 +1568,12 @@ CREATE TABLE IDN_RULE_REFERENCES ( IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[APP_GROUP_ASSOCIATION]') AND TYPE IN (N'U')) CREATE TABLE APP_GROUP_ASSOCIATION ( ID INTEGER NOT NULL IDENTITY, - APP_ID CHAR (36) NOT NULL, + APP_ID INTEGER NOT NULL, GROUP_ID VARCHAR (255) NOT NULL, - UM_DOMAIN_ID INTEGER NOT NULL, + DOMAIN_NAME VARCHAR (255) NOT NULL, PRIMARY KEY (ID), - FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, - CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, UM_DOMAIN_ID) + FOREIGN KEY (APP_ID) REFERENCES SP_APP(ID) ON DELETE CASCADE, + CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, DOMAIN_NAME) ); -- --------------------------- INDEX CREATION ----------------------------- diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql-cluster.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql-cluster.sql index 58180a202f03..a20bd7522039 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql-cluster.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql-cluster.sql @@ -1578,12 +1578,12 @@ CREATE TABLE IF NOT EXISTS IDN_RULE_REFERENCES ( CREATE TABLE IF NOT EXISTS APP_GROUP_ASSOCIATION ( ID INTEGER NOT NULL AUTO_INCREMENT, - APP_ID CHAR (36) NOT NULL, + APP_ID INTEGER NOT NULL, GROUP_ID VARCHAR (255) NOT NULL, - UM_DOMAIN_ID INTEGER NOT NULL, + DOMAIN_NAME VARCHAR (255) NOT NULL, PRIMARY KEY (ID), - FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, - CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, UM_DOMAIN_ID) + FOREIGN KEY (APP_ID) REFERENCES SP_APP(ID) ON DELETE CASCADE, + CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, DOMAIN_NAME) )ENGINE NDB; -- --------------------------- INDEX CREATION ----------------------------- diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql index 8e17353460bb..a277bb55839d 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/mysql.sql @@ -1446,12 +1446,12 @@ CREATE TABLE IF NOT EXISTS IDN_RULE_REFERENCES ( CREATE TABLE IF NOT EXISTS APP_GROUP_ASSOCIATION ( ID INTEGER NOT NULL AUTO_INCREMENT, - APP_ID CHAR (36) NOT NULL, + APP_ID INTEGER NOT NULL, GROUP_ID VARCHAR (255) NOT NULL, - UM_DOMAIN_ID INTEGER NOT NULL, + DOMAIN_NAME VARCHAR (255) NOT NULL, PRIMARY KEY (ID), - FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, - CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, UM_DOMAIN_ID) + FOREIGN KEY (APP_ID) REFERENCES SP_APP(ID) ON DELETE CASCADE, + CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, DOMAIN_NAME) )DEFAULT CHARACTER SET latin1 ENGINE INNODB; -- --------------------------- INDEX CREATION ----------------------------- diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle.sql index 295da3533ff2..c72db84401ea 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle.sql @@ -2229,12 +2229,12 @@ CREATE OR REPLACE TRIGGER IDN_RULE_REFERENCES_TRIGGER CREATE TABLE APP_GROUP_ASSOCIATION ( ID INTEGER NOT NULL, - APP_ID CHAR (36) NOT NULL, + APP_ID INTEGER NOT NULL, GROUP_ID VARCHAR (255) NOT NULL, - UM_DOMAIN_ID INTEGER NOT NULL, + DOMAIN_NAME VARCHAR (255) NOT NULL, PRIMARY KEY (ID), - FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, - CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, UM_DOMAIN_ID)) + FOREIGN KEY (APP_ID) REFERENCES SP_APP(ID) ON DELETE CASCADE, + CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, DOMAIN_NAME)) / CREATE SEQUENCE APP_GROUP_ASSOCIATION_SEQ START WITH 1 INCREMENT BY 1 NOCACHE / diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle_rac.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle_rac.sql index 86640e906d94..c9294c89d943 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle_rac.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/oracle_rac.sql @@ -2161,12 +2161,12 @@ CREATE OR REPLACE TRIGGER IDN_RULE_REFERENCES_TRIGGER / CREATE TABLE APP_GROUP_ASSOCIATION ( ID INTEGER NOT NULL, - APP_ID CHAR (36) NOT NULL, + APP_ID INTEGER NOT NULL, GROUP_ID VARCHAR (255) NOT NULL, - UM_DOMAIN_ID INTEGER NOT NULL, + DOMAIN_NAME VARCHAR (255) NOT NULL, PRIMARY KEY (ID), - FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, - CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, UM_DOMAIN_ID)) + FOREIGN KEY (APP_ID) REFERENCES SP_APP(ID) ON DELETE CASCADE, + CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, DOMAIN_NAME)) / CREATE SEQUENCE APP_GROUP_ASSOCIATION_SEQ START WITH 1 INCREMENT BY 1 CACHE 20 / diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/postgresql.sql b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/postgresql.sql index 338f3fec2fb3..b60d9fd479fa 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/postgresql.sql +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/dbscripts/postgresql.sql @@ -1696,12 +1696,12 @@ DROP SEQUENCE IF EXISTS APP_GROUP_ASSOCIATION_SEQ; CREATE SEQUENCE APP_GROUP_ASSOCIATION_SEQ; CREATE TABLE APP_GROUP_ASSOCIATION ( ID INTEGER DEFAULT NEXTVAL('APP_GROUP_ASSOCIATION_SEQ'), - APP_ID CHAR(36) NOT NULL, + APP_ID INTEGER NOT NULL, GROUP_ID VARCHAR (255) NOT NULL, - UM_DOMAIN_ID INTEGER NOT NULL, + DOMAIN_NAME VARCHAR (255) NOT NULL, PRIMARY KEY (ID), - FOREIGN KEY (APP_ID) REFERENCES SP_APP(UUID) ON DELETE CASCADE, - CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, UM_DOMAIN_ID) + FOREIGN KEY (APP_ID) REFERENCES SP_APP(ID) ON DELETE CASCADE, + CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, DOMAIN_NAME) ); -- --------------------------- INDEX CREATION ----------------------------- From 3a3cc93ffe6d7a24aecaa8c352313dd0e538b339 Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:40:50 +0530 Subject: [PATCH 04/23] remove unnecessary imports --- .../mgt/internal/ApplicationManagementServiceComponent.java | 1 - .../internal/ApplicationManagementServiceComponentHolder.java | 1 - 2 files changed, 2 deletions(-) diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java index 843640c705cf..c8e6a6287ae2 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java @@ -74,7 +74,6 @@ import org.wso2.carbon.identity.organization.management.service.OrganizationManagementInitialize; import org.wso2.carbon.identity.organization.management.service.OrganizationManager; import org.wso2.carbon.identity.organization.management.service.OrganizationUserResidentResolverService; -import org.wso2.carbon.identity.role.v2.mgt.core.GroupManagementService; import org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService; import org.wso2.carbon.identity.role.v2.mgt.core.listener.RoleManagementListener; import org.wso2.carbon.identity.secret.mgt.core.SecretManager; diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java index a328bd4e869a..8abcbdf71b62 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java @@ -29,7 +29,6 @@ import org.wso2.carbon.identity.organization.management.service.OrganizationManagementInitialize; import org.wso2.carbon.identity.organization.management.service.OrganizationManager; import org.wso2.carbon.identity.organization.management.service.OrganizationUserResidentResolverService; -import org.wso2.carbon.identity.role.v2.mgt.core.GroupManagementService; import org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService; import org.wso2.carbon.identity.secret.mgt.core.SecretManager; import org.wso2.carbon.identity.secret.mgt.core.SecretResolveManager; From a771053734248a2846015f0e2eb442bb14b3b887 Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Tue, 21 Jan 2025 23:35:39 +0530 Subject: [PATCH 05/23] implement groups meta service for applications --- .../application/mgt/ApplicationConstants.java | 20 +++++++-- .../mgt/ApplicationManagementService.java | 17 +++++++ .../mgt/ApplicationManagementServiceImpl.java | 45 +++++++++++++++++++ .../application/mgt/ApplicationMgtUtil.java | 4 -- .../mgt/dao/impl/ApplicationDAOImpl.java | 5 ++- .../DefaultApplicationValidator.java | 22 +++++---- 6 files changed, 91 insertions(+), 22 deletions(-) diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java index 93c5be573ef2..fb0ea7873a58 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java @@ -22,6 +22,9 @@ import java.io.File; +import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.Error.INVALID_FILTER; +import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.Error.UNEXPECTED_SERVER_ERROR; + /** * Definitions of few constants shared across with other components from this component. * You may not instantiate this class directly. However you can access the constants declared as static. @@ -75,6 +78,8 @@ private ApplicationConstants() { public static final String PURPOSE_GROUP_TYPE_SYSTEM = "SYSTEM"; public static final String PURPOSE_GROUP_SHARED = "SHARED"; public static final String IS_FRAGMENT_APP = "isFragmentApp"; + public static final String DISPLAY_NAME = "displayName"; + public static final String NAME = "name"; public static final String TENANT_DEFAULT_SP_TEMPLATE_NAME = "default"; public static final String MY_SQL = "MySQL"; @@ -100,6 +105,9 @@ private ApplicationConstants() { public static final String ONE_BASED_START_INDEX = "ONE_BASED_START_INDEX"; public static final String END_INDEX = "END_INDEX"; + // Filtering operations. + public static final String FILTER_SW = " sw "; + // System application config elements public static final String SYSTEM_APPLICATIONS_CONFIG_ELEMENT = "SystemApplications"; public static final String DEFAULT_APPLICATIONS_CONFIG_ELEMENT = "DefaultApplications"; @@ -262,16 +270,20 @@ public enum ErrorMessage { "Error occurred while retrieving user by userid: %s."), NON_EXISTING_USER_ID("60504", "User not found", "No user found for the given user-id: %s."), - UNSUPPORTED_USER_STORE_MANAGER("65003", "Unsupported user store manager.", + UNSUPPORTED_USER_STORE_MANAGER(UNEXPECTED_SERVER_ERROR.getCode(), "Unsupported user store manager.", "The underlying user store manager is not a unique ID user store manager for the tenant: %s."), ERROR_RETRIEVING_USERSTORE_MANAGER("65504", "Error retrieving userstore manager.", "Error occurred while retrieving userstore manager for the tenant: %s."), UNEXPECTED_ERROR("65006", "Unexpected processing error.", "Server encountered an unexpected error when creating the application."), - ERROR_CHECKING_GROUP_EXISTENCE("65007", "Unexpected processing error.", + ERROR_CHECKING_GROUP_EXISTENCE(UNEXPECTED_SERVER_ERROR.getCode(), "Unexpected processing error.", "Error occurred while checking the existence of the group: %s."), - ERROR_CHECKING_USER_STORE_EXISTENCE("65008", "Unexpected processing error.", - "Error occurred while checking the existence of the user store: %s."); + ERROR_CHECKING_USER_STORE_EXISTENCE(UNEXPECTED_SERVER_ERROR.getCode(), "Unexpected processing error.", + "Error occurred while checking the existence of the user store: %s."), + INVALID_GROUP_FILTER(INVALID_FILTER.getCode(), "Invalid filter query.", + "Filtering is only supported with 'name' attribute and 'sw' operation. Eg: name+sw+group1"), + ERROR_RETRIEVING_GROUP_LIST(UNEXPECTED_SERVER_ERROR.getCode(), "Error retrieving group list.", + "Error while retrieving group list for the user store: %s."); private final String code; diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementService.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementService.java index d1cfbf521f8e..a320f93de6df 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementService.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementService.java @@ -22,6 +22,7 @@ import org.wso2.carbon.identity.application.common.IdentityApplicationManagementServerException; import org.wso2.carbon.identity.application.common.model.ApplicationBasicInfo; import org.wso2.carbon.identity.application.common.model.AuthenticationStep; +import org.wso2.carbon.identity.application.common.model.GroupBasicInfo; import org.wso2.carbon.identity.application.common.model.IdentityProvider; import org.wso2.carbon.identity.application.common.model.ImportResponse; import org.wso2.carbon.identity.application.common.model.LocalAuthenticatorConfig; @@ -33,6 +34,7 @@ import org.wso2.carbon.identity.application.common.model.TrustedApp; import org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.PlatformType; import org.wso2.carbon.identity.application.mgt.internal.ApplicationManagementServiceComponentHolder; +import org.wso2.carbon.identity.core.model.Node; import org.wso2.carbon.idp.mgt.model.ConnectedAppsResult; import java.util.ArrayList; @@ -631,5 +633,20 @@ public void addAssociatedRoleToApplication(ServiceProvider serviceProvider, Stri throw new NotImplementedException(); } + + /** + * Get the groups list based on the filter. + * + * @param tenantDomain Tenant domain. + * @param domainName User store domain name. + * @param filter Filtering that should be applied. + * @return The list of groups. + * @throws IdentityApplicationManagementException If an error occurred while retrieving groups list. + */ + public List getGroups(String tenantDomain, String domainName, Node filter) + throws IdentityApplicationManagementException { + + throw new NotImplementedException(); + } } diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java index 1917f3f1f28f..aec409e1dfa9 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java @@ -39,6 +39,7 @@ import org.wso2.carbon.identity.application.common.model.AssociatedRolesConfig; import org.wso2.carbon.identity.application.common.model.AuthenticationStep; import org.wso2.carbon.identity.application.common.model.DefaultAuthenticationSequence; +import org.wso2.carbon.identity.application.common.model.GroupBasicInfo; import org.wso2.carbon.identity.application.common.model.IdentityProvider; import org.wso2.carbon.identity.application.common.model.ImportResponse; import org.wso2.carbon.identity.application.common.model.InboundAuthenticationConfig; @@ -84,6 +85,8 @@ import org.wso2.carbon.identity.central.log.mgt.utils.LogConstants; import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils; import org.wso2.carbon.identity.claim.metadata.mgt.model.LocalClaim; +import org.wso2.carbon.identity.core.model.ExpressionNode; +import org.wso2.carbon.identity.core.model.Node; import org.wso2.carbon.identity.core.util.IdentityConfigParser; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.core.util.IdentityUtil; @@ -107,6 +110,10 @@ import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.core.UserCoreConstants; import org.wso2.carbon.user.core.common.AbstractUserStoreManager; +import org.wso2.carbon.user.core.common.Group; +import org.wso2.carbon.user.core.model.Condition; +import org.wso2.carbon.user.core.model.ExpressionCondition; +import org.wso2.carbon.user.core.model.ExpressionOperation; import org.wso2.carbon.utils.AuditLog; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; import org.wso2.carbon.utils.multitenancy.MultitenantUtils; @@ -162,7 +169,12 @@ import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.PlatformType; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.APPLICATION_NAME_CONFIG_ELEMENT; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.DEFAULT_APPLICATIONS_CONFIG_ELEMENT; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.DISPLAY_NAME; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.ERROR_RETRIEVING_GROUP_LIST; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.INVALID_GROUP_FILTER; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.FILTER_SW; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.IS_FRAGMENT_APP; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.NAME; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.SYSTEM_APPLICATIONS_CONFIG_ELEMENT; import static org.wso2.carbon.identity.application.mgt.ApplicationMgtUtil.buildSPData; import static org.wso2.carbon.identity.application.mgt.ApplicationMgtUtil.endTenantFlow; @@ -3261,6 +3273,39 @@ public void deleteApplicationByResourceId(String resourceId, } } + @Override + public List getGroups(String tenantDomain, String domainName, Node filter) + throws IdentityApplicationManagementException { + + Condition filterCondition = null; + if (filter != null) { + if (!(filter instanceof ExpressionNode) || + !StringUtils.equals(((ExpressionNode) filter).getAttributeValue(), NAME) || + !StringUtils.equals(((ExpressionNode) filter).getOperation(), FILTER_SW)) { + throw new IdentityApplicationManagementClientException(INVALID_GROUP_FILTER.getCode(), + INVALID_GROUP_FILTER.getDescription()); + } + filterCondition = new ExpressionCondition(ExpressionOperation.SW.toString(), DISPLAY_NAME, + ((ExpressionNode) filter).getValue()); + } + AbstractUserStoreManager userStoreManager = ApplicationMgtUtil.getUserStoreManager(tenantDomain); + try { + List groupBasicInfos = new ArrayList<>(); + // Group endpoint does not support pagination. + List groups = userStoreManager.listGroups(filterCondition, domainName, 0, 0, null, null); + for (Group group : groups) { + GroupBasicInfo groupBasicInfo = new GroupBasicInfo(); + groupBasicInfo.setId(group.getGroupID()); + groupBasicInfo.setName(group.getGroupName()); + groupBasicInfos.add(groupBasicInfo); + } + return groupBasicInfos; + } catch (UserStoreException e) { + throw new IdentityApplicationManagementServerException(ERROR_RETRIEVING_GROUP_LIST.getCode(), + String.format(ERROR_RETRIEVING_GROUP_LIST.getDescription(), domainName), e); + } + } + private void doPreDeleteChecks(String applicationName, String tenantDomain, String username) throws IdentityApplicationManagementException { diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtil.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtil.java index de76c379277e..68d239750554 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtil.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtil.java @@ -57,7 +57,6 @@ import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException; -import org.wso2.carbon.identity.role.v2.mgt.core.exception.IdentityRoleManagementServerException; import org.wso2.carbon.user.api.Tenant; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.api.UserStoreManager; @@ -1290,17 +1289,14 @@ public static AbstractUserStoreManager getUserStoreManager(String tenantDomain) .getUserStoreManager(); } catch (UserStoreException e) { throw new IdentityApplicationManagementException(ERROR_RETRIEVING_USERSTORE_MANAGER.getCode(), - ERROR_RETRIEVING_USERSTORE_MANAGER.getMessage(), String.format(ERROR_RETRIEVING_USERSTORE_MANAGER.getDescription(), tenantDomain), e); } if (userStoreManager == null) { throw new IdentityApplicationManagementException(ERROR_RETRIEVING_USERSTORE_MANAGER.getCode(), - ERROR_RETRIEVING_USERSTORE_MANAGER.getMessage(), String.format(ERROR_RETRIEVING_USERSTORE_MANAGER.getDescription(), tenantDomain)); } if (!(userStoreManager instanceof AbstractUserStoreManager)) { throw new IdentityApplicationManagementException(UNSUPPORTED_USER_STORE_MANAGER.getCode(), - UNSUPPORTED_USER_STORE_MANAGER.getMessage(), String.format(UNSUPPORTED_USER_STORE_MANAGER.getDescription(), tenantDomain)); } return (AbstractUserStoreManager) userStoreManager; diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java index f489f7e4dbc6..5ca26084f624 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java @@ -18,7 +18,6 @@ package org.wso2.carbon.identity.application.mgt.dao.impl; -import java.util.Locale; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.ArrayUtils; @@ -130,6 +129,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.OptionalInt; @@ -2008,7 +2008,8 @@ private ServiceProvider getBasicApplicationData(String applicationName, Connecti serviceProvider.setDiscoverable(getBooleanValue(basicAppDataResultSet.getString(ApplicationTableColumns .IS_DISCOVERABLE))); - serviceProvider.setDiscoverableGroups(getDiscoverableGroups(serviceProvider.getApplicationID(), connection, tenantDomain)); + serviceProvider.setDiscoverableGroups( + getDiscoverableGroups(serviceProvider.getApplicationID(), connection, tenantDomain)); User owner = new User(); owner.setUserName(basicAppDataResultSet.getString(5)); diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java index b0aa48bb1c97..c9a97c284624 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java @@ -19,7 +19,6 @@ package org.wso2.carbon.identity.application.mgt.validator; -import java.util.Objects; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; @@ -58,7 +57,6 @@ import org.wso2.carbon.identity.claim.metadata.mgt.exception.ClaimMetadataException; import org.wso2.carbon.identity.claim.metadata.mgt.model.ClaimDialect; import org.wso2.carbon.identity.core.util.IdentityUtil; -import org.wso2.carbon.identity.user.store.configuration.dto.UserStoreDTO; import org.wso2.carbon.identity.user.store.configuration.utils.IdentityUserStoreClientException; import org.wso2.carbon.identity.user.store.configuration.utils.IdentityUserStoreMgtException; import org.wso2.carbon.idp.mgt.IdentityProviderManagementException; @@ -66,6 +64,9 @@ import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.api.UserStoreManager; import org.wso2.carbon.user.core.UserCoreConstants; +import org.wso2.carbon.user.core.UserStoreClientException; +import org.wso2.carbon.user.core.common.AbstractUserStoreManager; +import org.wso2.carbon.user.core.util.UserCoreUtil; import java.util.ArrayList; import java.util.Arrays; @@ -75,9 +76,6 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.wso2.carbon.user.core.UserStoreClientException; -import org.wso2.carbon.user.core.common.AbstractUserStoreManager; -import org.wso2.carbon.user.core.util.UserCoreUtil; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.ERROR_CHECKING_GROUP_EXISTENCE; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.ERROR_CHECKING_USER_STORE_EXISTENCE; @@ -201,7 +199,8 @@ private void validateApplicationVersion(List validationErrors, ServicePr * @param validationErrors List of validation errors. * @param serviceProvider Service provider configuration. * @param tenantDomain Tenant domain of the application. - * @throws IdentityApplicationManagementException If an error occurs while validating the discoverability configurations. + * @throws IdentityApplicationManagementException If an error occurs while validating the discoverability + * configurations. */ private void validateDiscoverabilityConfigs(List validationErrors, ServiceProvider serviceProvider, String tenantDomain) throws IdentityApplicationManagementException { @@ -245,14 +244,13 @@ private void validateDiscoverableGroups(List validationErrors, ServicePr } try { ApplicationManagementServiceComponentHolder.getInstance().getUserStoreConfigService() - .getUserStore(discoverableGroup.getUserStore()); + .getUserStore(discoverableGroup.getUserStore()); } catch (IdentityUserStoreMgtException e) { if (e instanceof IdentityUserStoreClientException) { validationErrors.add(String.format(USER_STORE_NOT_FOUND, i)); continue; } throw new IdentityApplicationManagementException(ERROR_CHECKING_USER_STORE_EXISTENCE.getCode(), - ERROR_CHECKING_USER_STORE_EXISTENCE.getMessage(), String.format(ERROR_CHECKING_USER_STORE_EXISTENCE.getDescription(), discoverableGroup.getUserStore()), e); } @@ -271,10 +269,11 @@ private void validateDiscoverableGroups(List validationErrors, ServicePr ApplicationMgtUtil.getUserStoreManager(tenantDomain); try { String groupName = userStoreManager.getGroupNameByGroupId( - UserCoreUtil.addDomainToName(groupBasicInfo.getId(), discoverableGroup.getUserStore())); + UserCoreUtil.addDomainToName( + groupBasicInfo.getId(), discoverableGroup.getUserStore())); String groupNameWithoutDomain = UserCoreUtil.removeDomainFromName(groupName); - if (!Objects.equals(groupNameWithoutDomain, groupBasicInfo.getName()) && - !Objects.equals(groupName, groupBasicInfo.getName())) { + if (!StringUtils.equals(groupNameWithoutDomain, groupBasicInfo.getName()) && + !StringUtils.equals(groupName, groupBasicInfo.getName())) { log.warn(String.format(GROUP_NAME_NOT_MATCH_WITH_GROUP_ID, groupBasicInfo.getName(), groupBasicInfo.getId())); } @@ -284,7 +283,6 @@ private void validateDiscoverableGroups(List validationErrors, ServicePr continue; } throw new IdentityApplicationManagementException(ERROR_CHECKING_GROUP_EXISTENCE.getCode(), - ERROR_CHECKING_GROUP_EXISTENCE.getMessage(), String.format(ERROR_CHECKING_GROUP_EXISTENCE.getDescription(), groupBasicInfo.getId()), e); } From 084644897627d63d9ca9a99e7f92ed582bb11be9 Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Wed, 22 Jan 2025 14:08:01 +0530 Subject: [PATCH 06/23] remove redundant discoverable groups resolving logic --- .../identity/application/mgt/dao/impl/ApplicationDAOImpl.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java index 5ca26084f624..8789ab40f21d 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java @@ -2241,8 +2241,6 @@ public ServiceProvider getApplication(int applicationId) throws IdentityApplicat if (serviceProvider == null) { return null; } - serviceProvider.setDiscoverableGroups( - getDiscoverableGroups(applicationId, connection, serviceProvider.getTenantDomain())); int tenantID = IdentityTenantUtil.getTenantId(serviceProvider.getTenantDomain()); List propertyList = getServicePropertiesBySpId(connection, applicationId); From ba14222d5ff3e4c224cbbdaa096bdf7660f38e1d Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Wed, 22 Jan 2025 18:01:41 +0530 Subject: [PATCH 07/23] remove domain name validation --- .../application/mgt/ApplicationConstants.java | 2 -- ...ApplicationManagementServiceComponent.java | 36 +++++++++---------- .../DefaultApplicationValidator.java | 32 ++--------------- 3 files changed, 21 insertions(+), 49 deletions(-) diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java index 30ce03b0b9ee..e8ac6ac35c14 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java @@ -283,8 +283,6 @@ public enum ErrorMessage { "Server encountered an unexpected error when creating the application."), ERROR_CHECKING_GROUP_EXISTENCE(UNEXPECTED_SERVER_ERROR.getCode(), "Unexpected processing error.", "Error occurred while checking the existence of the group: %s."), - ERROR_CHECKING_USER_STORE_EXISTENCE(UNEXPECTED_SERVER_ERROR.getCode(), "Unexpected processing error.", - "Error occurred while checking the existence of the user store: %s."), INVALID_GROUP_FILTER(INVALID_FILTER.getCode(), "Invalid filter query.", "Filtering is only supported with 'name' attribute and 'sw' operation. Eg: name+sw+group1"), ERROR_RETRIEVING_GROUP_LIST(UNEXPECTED_SERVER_ERROR.getCode(), "Error retrieving group list.", diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java index 6146f8a412f2..740b72086875 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java @@ -80,7 +80,7 @@ import org.wso2.carbon.identity.role.v2.mgt.core.listener.RoleManagementListener; import org.wso2.carbon.identity.secret.mgt.core.SecretManager; import org.wso2.carbon.identity.secret.mgt.core.SecretResolveManager; -import org.wso2.carbon.identity.user.store.configuration.UserStoreConfigService; +//import org.wso2.carbon.identity.user.store.configuration.UserStoreConfigService; import org.wso2.carbon.idp.mgt.listener.IdentityProviderMgtListener; import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.utils.CarbonUtils; @@ -664,21 +664,21 @@ protected void unsetApplicationCertificateManagementService(ApplicationCertifica log.debug("ApplicationCertificateManagementService unset in ApplicationManagementServiceComponent bundle."); } - @Reference( - name = "org.wso2.carbon.identity.user.store.configuration.UserStoreConfigService", - service = org.wso2.carbon.identity.user.store.configuration.UserStoreConfigService.class, - cardinality = ReferenceCardinality.MANDATORY, - policy = ReferencePolicy.DYNAMIC, - unbind = "unsetUserStoreConfigService") - protected void setUserStoreConfigService(UserStoreConfigService userStoreConfigService) { - - ApplicationManagementServiceComponentHolder.getInstance().setUserStoreConfigService(userStoreConfigService); - log.debug("UserStoreConfigService set in ApplicationManagementServiceComponent bundle."); - } - - protected void unsetUserStoreConfigService(UserStoreConfigService userStoreConfigService) { - - ApplicationManagementServiceComponentHolder.getInstance().setUserStoreConfigService(null); - log.debug("UserStoreConfigService unset in ApplicationManagementServiceComponent bundle."); - } +// @Reference( +// name = "org.wso2.carbon.identity.user.store.configuration.UserStoreConfigService", +// service = org.wso2.carbon.identity.user.store.configuration.UserStoreConfigService.class, +// cardinality = ReferenceCardinality.MANDATORY, +// policy = ReferencePolicy.DYNAMIC, +// unbind = "unsetUserStoreConfigService") +// protected void setUserStoreConfigService(UserStoreConfigService userStoreConfigService) { +// +// ApplicationManagementServiceComponentHolder.getInstance().setUserStoreConfigService(userStoreConfigService); +// log.debug("UserStoreConfigService set in ApplicationManagementServiceComponent bundle."); +// } +// +// protected void unsetUserStoreConfigService(UserStoreConfigService userStoreConfigService) { +// +// ApplicationManagementServiceComponentHolder.getInstance().setUserStoreConfigService(null); +// log.debug("UserStoreConfigService unset in ApplicationManagementServiceComponent bundle."); +// } } diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java index c9a97c284624..2359dbfd8508 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java @@ -52,13 +52,10 @@ import org.wso2.carbon.identity.application.mgt.ApplicationMgtUtil; import org.wso2.carbon.identity.application.mgt.dao.ApplicationDAO; import org.wso2.carbon.identity.application.mgt.dao.impl.ApplicationDAOImpl; -import org.wso2.carbon.identity.application.mgt.internal.ApplicationManagementServiceComponentHolder; import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataManagementServiceImpl; import org.wso2.carbon.identity.claim.metadata.mgt.exception.ClaimMetadataException; import org.wso2.carbon.identity.claim.metadata.mgt.model.ClaimDialect; import org.wso2.carbon.identity.core.util.IdentityUtil; -import org.wso2.carbon.identity.user.store.configuration.utils.IdentityUserStoreClientException; -import org.wso2.carbon.identity.user.store.configuration.utils.IdentityUserStoreMgtException; import org.wso2.carbon.idp.mgt.IdentityProviderManagementException; import org.wso2.carbon.idp.mgt.IdentityProviderManager; import org.wso2.carbon.user.api.UserStoreException; @@ -78,7 +75,6 @@ import java.util.stream.Stream; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.ERROR_CHECKING_GROUP_EXISTENCE; -import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.ERROR_CHECKING_USER_STORE_EXISTENCE; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.TRUSTED_APP_MAX_THUMBPRINT_COUNT_PROPERTY; import static org.wso2.carbon.user.core.UserCoreConstants.INTERNAL_DOMAIN; import static org.wso2.carbon.user.core.UserCoreConstants.WORKFLOW_DOMAIN; @@ -119,14 +115,10 @@ public class DefaultApplicationValidator implements ApplicationValidator { private static final String EMPTY_DISCOVERABLE_GROUPS = "The list of discoverable groups is empty."; private static final String NO_USER_STORE_FOR_THE_DISCOVERABLE_GROUP = "No user store defined for the discoverable groups indexed at %d."; - private static final String USER_STORE_NOT_FOUND = - "The provided user store is not found for the discoverable groups indexed at %d."; private static final String NO_GROUPS_FOR_THE_DISCOVERABLE_GROUP = "No groups defined for the user store: '%s' in the discoverable groups configuration."; private static final String NO_GROUP_ID = "Group ID is not defined for the group indexed at %d for the user " + "store: '%s' in the discoverable groups configuration."; - private static final String GROUP_NAME_NOT_MATCH_WITH_GROUP_ID = "Group name '%s' does not match with the user " + - "store group name for the group ID '%s' in the discoverable groups configuration."; private static final String NO_GROUP_WITH_GIVEN_ID = "No group found for the given group ID: '%s'."; public static final String IS_HANDLER = "IS_HANDLER"; private static Pattern loopPattern; @@ -238,22 +230,10 @@ private void validateDiscoverableGroups(List validationErrors, ServicePr for (int i = 0; i < discoverableGroups.length; i++) { DiscoverableGroup discoverableGroup = discoverableGroups[i]; GroupBasicInfo[] groupBasicInfos = discoverableGroup.getGroups(); - if (discoverableGroup.getUserStore() == null) { + if (StringUtils.isBlank(discoverableGroup.getUserStore())) { validationErrors.add(String.format(NO_USER_STORE_FOR_THE_DISCOVERABLE_GROUP, i)); continue; } - try { - ApplicationManagementServiceComponentHolder.getInstance().getUserStoreConfigService() - .getUserStore(discoverableGroup.getUserStore()); - } catch (IdentityUserStoreMgtException e) { - if (e instanceof IdentityUserStoreClientException) { - validationErrors.add(String.format(USER_STORE_NOT_FOUND, i)); - continue; - } - throw new IdentityApplicationManagementException(ERROR_CHECKING_USER_STORE_EXISTENCE.getCode(), - String.format(ERROR_CHECKING_USER_STORE_EXISTENCE.getDescription(), - discoverableGroup.getUserStore()), e); - } if (groupBasicInfos == null || groupBasicInfos.length == 0) { validationErrors.add( String.format(NO_GROUPS_FOR_THE_DISCOVERABLE_GROUP, discoverableGroup.getUserStore())); @@ -261,22 +241,16 @@ private void validateDiscoverableGroups(List validationErrors, ServicePr } for (int j = 0; j < groupBasicInfos.length; j++) { GroupBasicInfo groupBasicInfo = groupBasicInfos[j]; - if (groupBasicInfo.getId() == null) { + if (StringUtils.isBlank(groupBasicInfo.getId())) { validationErrors.add(String.format(NO_GROUP_ID, j, discoverableGroup.getUserStore())); continue; } AbstractUserStoreManager userStoreManager = ApplicationMgtUtil.getUserStoreManager(tenantDomain); try { - String groupName = userStoreManager.getGroupNameByGroupId( + userStoreManager.getGroupNameByGroupId( UserCoreUtil.addDomainToName( groupBasicInfo.getId(), discoverableGroup.getUserStore())); - String groupNameWithoutDomain = UserCoreUtil.removeDomainFromName(groupName); - if (!StringUtils.equals(groupNameWithoutDomain, groupBasicInfo.getName()) && - !StringUtils.equals(groupName, groupBasicInfo.getName())) { - log.warn(String.format(GROUP_NAME_NOT_MATCH_WITH_GROUP_ID, groupBasicInfo.getName(), - groupBasicInfo.getId())); - } } catch (UserStoreException e) { if (e instanceof UserStoreClientException) { validationErrors.add(String.format(NO_GROUP_WITH_GIVEN_ID, groupBasicInfo.getId())); From 2fe16bfeffb5258378bddf784b07cc30a7bbd47f Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Sat, 25 Jan 2025 14:24:53 +0530 Subject: [PATCH 08/23] add domain name validation --- .../application/mgt/ApplicationConstants.java | 10 ++++--- .../mgt/ApplicationManagementServiceImpl.java | 26 ++++++++++++++----- ...ApplicationManagementServiceComponent.java | 19 -------------- .../DefaultApplicationValidator.java | 9 +++++-- pom.xml | 2 +- 5 files changed, 34 insertions(+), 32 deletions(-) diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java index e8ac6ac35c14..d8f15b6ca5d7 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java @@ -23,6 +23,7 @@ import java.io.File; import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.Error.INVALID_FILTER; +import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.Error.INVALID_REQUEST; import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.Error.UNEXPECTED_SERVER_ERROR; /** @@ -78,7 +79,6 @@ private ApplicationConstants() { public static final String PURPOSE_GROUP_TYPE_SYSTEM = "SYSTEM"; public static final String PURPOSE_GROUP_SHARED = "SHARED"; public static final String IS_FRAGMENT_APP = "isFragmentApp"; - public static final String DISPLAY_NAME = "displayName"; public static final String NAME = "name"; public static final String TENANT_DEFAULT_SP_TEMPLATE_NAME = "default"; @@ -106,7 +106,7 @@ private ApplicationConstants() { public static final String END_INDEX = "END_INDEX"; // Filtering operations. - public static final String FILTER_SW = " sw "; + public static final String FILTER_CO = "co"; // System application config elements public static final String SYSTEM_APPLICATIONS_CONFIG_ELEMENT = "SystemApplications"; @@ -284,9 +284,11 @@ public enum ErrorMessage { ERROR_CHECKING_GROUP_EXISTENCE(UNEXPECTED_SERVER_ERROR.getCode(), "Unexpected processing error.", "Error occurred while checking the existence of the group: %s."), INVALID_GROUP_FILTER(INVALID_FILTER.getCode(), "Invalid filter query.", - "Filtering is only supported with 'name' attribute and 'sw' operation. Eg: name+sw+group1"), + "Filtering is only supported with 'name' attribute and 'co' operation. Eg: name+co+group1"), ERROR_RETRIEVING_GROUP_LIST(UNEXPECTED_SERVER_ERROR.getCode(), "Error retrieving group list.", - "Error while retrieving group list for the user store: %s."); + "Error while retrieving group list for the user store: %s."), + INVALID_USER_STORE_DOMAIN(INVALID_REQUEST.getCode(), "Invalid user store domain.", + "User store domain: %s is not valid for the tenant: %s."); private final String code; diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java index aec409e1dfa9..bb5d500f5ef7 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java @@ -114,6 +114,7 @@ import org.wso2.carbon.user.core.model.Condition; import org.wso2.carbon.user.core.model.ExpressionCondition; import org.wso2.carbon.user.core.model.ExpressionOperation; +import org.wso2.carbon.user.core.util.UserCoreUtil; import org.wso2.carbon.utils.AuditLog; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; import org.wso2.carbon.utils.multitenancy.MultitenantUtils; @@ -169,10 +170,10 @@ import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.PlatformType; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.APPLICATION_NAME_CONFIG_ELEMENT; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.DEFAULT_APPLICATIONS_CONFIG_ELEMENT; -import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.DISPLAY_NAME; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.ERROR_RETRIEVING_GROUP_LIST; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.INVALID_GROUP_FILTER; -import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.FILTER_SW; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.INVALID_USER_STORE_DOMAIN; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.FILTER_CO; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.IS_FRAGMENT_APP; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.NAME; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.SYSTEM_APPLICATIONS_CONFIG_ELEMENT; @@ -190,6 +191,7 @@ import static org.wso2.carbon.identity.core.util.IdentityUtil.getInitiatorId; import static org.wso2.carbon.identity.role.v2.mgt.core.RoleConstants.Error.ROLE_MANAGEMENT_ERROR_CODE_PREFIX; import static org.wso2.carbon.identity.role.v2.mgt.core.RoleConstants.Error.ROLE_NOT_FOUND; +import static org.wso2.carbon.user.core.UserStoreConfigConstants.GROUP_NAME_ATTRIBUTE; import static org.wso2.carbon.utils.multitenancy.MultitenantConstants.SUPER_TENANT_DOMAIN_NAME; /** @@ -3277,26 +3279,38 @@ public void deleteApplicationByResourceId(String resourceId, public List getGroups(String tenantDomain, String domainName, Node filter) throws IdentityApplicationManagementException { - Condition filterCondition = null; + if (StringUtils.isBlank(domainName)) { + tenantDomain = IdentityUtil.getPrimaryDomainName(); + } + // Initialize filter condition with a default value to ensure it matches any content if null. + Condition filterCondition = + new ExpressionCondition(ExpressionOperation.SW.toString(), GROUP_NAME_ATTRIBUTE, StringUtils.EMPTY); + // This service method is designed to retrieve the group list for applications and only supports + // the 'co' (contains) operation with the 'name' attribute of the group. if (filter != null) { if (!(filter instanceof ExpressionNode) || !StringUtils.equals(((ExpressionNode) filter).getAttributeValue(), NAME) || - !StringUtils.equals(((ExpressionNode) filter).getOperation(), FILTER_SW)) { + !StringUtils.equals(((ExpressionNode) filter).getOperation(), FILTER_CO)) { throw new IdentityApplicationManagementClientException(INVALID_GROUP_FILTER.getCode(), INVALID_GROUP_FILTER.getDescription()); } - filterCondition = new ExpressionCondition(ExpressionOperation.SW.toString(), DISPLAY_NAME, + filterCondition = new ExpressionCondition(ExpressionOperation.CO.toString(), GROUP_NAME_ATTRIBUTE, ((ExpressionNode) filter).getValue()); } AbstractUserStoreManager userStoreManager = ApplicationMgtUtil.getUserStoreManager(tenantDomain); try { + // Validate the user store domain. + if (userStoreManager.getSecondaryUserStoreManager(domainName) == null) { + throw new IdentityApplicationManagementClientException(INVALID_USER_STORE_DOMAIN.getCode(), + String.format(INVALID_USER_STORE_DOMAIN.getDescription(), domainName, tenantDomain)); + } List groupBasicInfos = new ArrayList<>(); // Group endpoint does not support pagination. List groups = userStoreManager.listGroups(filterCondition, domainName, 0, 0, null, null); for (Group group : groups) { GroupBasicInfo groupBasicInfo = new GroupBasicInfo(); groupBasicInfo.setId(group.getGroupID()); - groupBasicInfo.setName(group.getGroupName()); + groupBasicInfo.setName(UserCoreUtil.removeDomainFromName(group.getGroupName())); groupBasicInfos.add(groupBasicInfo); } return groupBasicInfos; diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java index 740b72086875..f2a057ab251c 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponent.java @@ -80,7 +80,6 @@ import org.wso2.carbon.identity.role.v2.mgt.core.listener.RoleManagementListener; import org.wso2.carbon.identity.secret.mgt.core.SecretManager; import org.wso2.carbon.identity.secret.mgt.core.SecretResolveManager; -//import org.wso2.carbon.identity.user.store.configuration.UserStoreConfigService; import org.wso2.carbon.idp.mgt.listener.IdentityProviderMgtListener; import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.utils.CarbonUtils; @@ -663,22 +662,4 @@ protected void unsetApplicationCertificateManagementService(ApplicationCertifica ApplicationManagementServiceComponentHolder.getInstance().setApplicationCertificateMgtService(null); log.debug("ApplicationCertificateManagementService unset in ApplicationManagementServiceComponent bundle."); } - -// @Reference( -// name = "org.wso2.carbon.identity.user.store.configuration.UserStoreConfigService", -// service = org.wso2.carbon.identity.user.store.configuration.UserStoreConfigService.class, -// cardinality = ReferenceCardinality.MANDATORY, -// policy = ReferencePolicy.DYNAMIC, -// unbind = "unsetUserStoreConfigService") -// protected void setUserStoreConfigService(UserStoreConfigService userStoreConfigService) { -// -// ApplicationManagementServiceComponentHolder.getInstance().setUserStoreConfigService(userStoreConfigService); -// log.debug("UserStoreConfigService set in ApplicationManagementServiceComponent bundle."); -// } -// -// protected void unsetUserStoreConfigService(UserStoreConfigService userStoreConfigService) { -// -// ApplicationManagementServiceComponentHolder.getInstance().setUserStoreConfigService(null); -// log.debug("UserStoreConfigService unset in ApplicationManagementServiceComponent bundle."); -// } } diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java index 2359dbfd8508..925612bc1cdd 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java @@ -115,6 +115,8 @@ public class DefaultApplicationValidator implements ApplicationValidator { private static final String EMPTY_DISCOVERABLE_GROUPS = "The list of discoverable groups is empty."; private static final String NO_USER_STORE_FOR_THE_DISCOVERABLE_GROUP = "No user store defined for the discoverable groups indexed at %d."; + private static final String USER_STORE_NOT_FOUND = + "The provided user store is not found for the discoverable groups indexed at %d."; private static final String NO_GROUPS_FOR_THE_DISCOVERABLE_GROUP = "No groups defined for the user store: '%s' in the discoverable groups configuration."; private static final String NO_GROUP_ID = "Group ID is not defined for the group indexed at %d for the user " + @@ -227,6 +229,7 @@ private void validateDiscoverableGroups(List validationErrors, ServicePr validationErrors.add(EMPTY_DISCOVERABLE_GROUPS); return; } + AbstractUserStoreManager userStoreManager = ApplicationMgtUtil.getUserStoreManager(tenantDomain); for (int i = 0; i < discoverableGroups.length; i++) { DiscoverableGroup discoverableGroup = discoverableGroups[i]; GroupBasicInfo[] groupBasicInfos = discoverableGroup.getGroups(); @@ -234,6 +237,10 @@ private void validateDiscoverableGroups(List validationErrors, ServicePr validationErrors.add(String.format(NO_USER_STORE_FOR_THE_DISCOVERABLE_GROUP, i)); continue; } + if (userStoreManager.getSecondaryUserStoreManager(discoverableGroup.getUserStore()) == null) { + validationErrors.add(String.format(USER_STORE_NOT_FOUND, i)); + continue; + } if (groupBasicInfos == null || groupBasicInfos.length == 0) { validationErrors.add( String.format(NO_GROUPS_FOR_THE_DISCOVERABLE_GROUP, discoverableGroup.getUserStore())); @@ -245,8 +252,6 @@ private void validateDiscoverableGroups(List validationErrors, ServicePr validationErrors.add(String.format(NO_GROUP_ID, j, discoverableGroup.getUserStore())); continue; } - AbstractUserStoreManager userStoreManager = - ApplicationMgtUtil.getUserStoreManager(tenantDomain); try { userStoreManager.getGroupNameByGroupId( UserCoreUtil.addDomainToName( diff --git a/pom.xml b/pom.xml index 59d5a0612814..869be37c6c20 100644 --- a/pom.xml +++ b/pom.xml @@ -1835,7 +1835,7 @@ UTF-8 - 4.10.28 + 4.10.32 4.7.0 [4.5.0, 5.0.0) [1.0.1, 2.0.0) From 9b66cae44e962f5b2edc4a79ae249f9ca5cc863e Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Sat, 25 Jan 2025 14:57:04 +0530 Subject: [PATCH 09/23] fix the domain name fallback issue --- .../application/mgt/ApplicationManagementServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java index bb5d500f5ef7..eb8e37d0711b 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java @@ -3280,7 +3280,7 @@ public List getGroups(String tenantDomain, String domainName, No throws IdentityApplicationManagementException { if (StringUtils.isBlank(domainName)) { - tenantDomain = IdentityUtil.getPrimaryDomainName(); + domainName = IdentityUtil.getPrimaryDomainName(); } // Initialize filter condition with a default value to ensure it matches any content if null. Condition filterCondition = From 8759bc023313dceec532778da2238520f5fec31b Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Sun, 26 Jan 2025 14:33:59 +0530 Subject: [PATCH 10/23] remove domain qualified group id creation --- .../application/mgt/dao/impl/ApplicationDAOImpl.java | 3 +-- .../mgt/validator/DefaultApplicationValidator.java | 9 +-------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java index 8789ab40f21d..fa44f68f37fb 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java @@ -6777,8 +6777,7 @@ private DiscoverableGroup[] getDiscoverableGroups(int applicationId, Connection GroupBasicInfo groupBasicInfo = new GroupBasicInfo(); groupBasicInfo.setId(groupID); try { - String groupName = userStoreManager.getGroupNameByGroupId( - UserCoreUtil.addDomainToName(groupID, domainName)); + String groupName = userStoreManager.getGroupNameByGroupId(groupID); groupBasicInfo.setName(UserCoreUtil.removeDomainFromName(groupName)); if (groupInfoMap.containsKey(domainName)) { groupInfoMap.get(domainName).add(groupBasicInfo); diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java index 925612bc1cdd..609d6b235d3a 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java @@ -112,7 +112,6 @@ public class DefaultApplicationValidator implements ApplicationValidator { "required when configuring an android application as a trusted mobile application."; private static final String DISCOVERABLE_GROUPS_FOR_NON_DISCOVERABLE_APPLICATION = "Discoverable groups are defined for a non-discoverable application."; - private static final String EMPTY_DISCOVERABLE_GROUPS = "The list of discoverable groups is empty."; private static final String NO_USER_STORE_FOR_THE_DISCOVERABLE_GROUP = "No user store defined for the discoverable groups indexed at %d."; private static final String USER_STORE_NOT_FOUND = @@ -225,10 +224,6 @@ private void validateDiscoverableGroups(List validationErrors, ServicePr validationErrors.add(DISCOVERABLE_GROUPS_FOR_NON_DISCOVERABLE_APPLICATION); return; } - if (discoverableGroups.length == 0) { - validationErrors.add(EMPTY_DISCOVERABLE_GROUPS); - return; - } AbstractUserStoreManager userStoreManager = ApplicationMgtUtil.getUserStoreManager(tenantDomain); for (int i = 0; i < discoverableGroups.length; i++) { DiscoverableGroup discoverableGroup = discoverableGroups[i]; @@ -253,9 +248,7 @@ private void validateDiscoverableGroups(List validationErrors, ServicePr continue; } try { - userStoreManager.getGroupNameByGroupId( - UserCoreUtil.addDomainToName( - groupBasicInfo.getId(), discoverableGroup.getUserStore())); + userStoreManager.getGroupNameByGroupId(groupBasicInfo.getId()); } catch (UserStoreException e) { if (e instanceof UserStoreClientException) { validationErrors.add(String.format(NO_GROUP_WITH_GIVEN_ID, groupBasicInfo.getId())); From 5478ee80a7e1b82287b00f794dce5a3d4b828524 Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Sun, 26 Jan 2025 14:47:20 +0530 Subject: [PATCH 11/23] remove unused import --- .../application/mgt/validator/DefaultApplicationValidator.java | 1 - 1 file changed, 1 deletion(-) diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java index 609d6b235d3a..60954c29f07b 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidator.java @@ -63,7 +63,6 @@ import org.wso2.carbon.user.core.UserCoreConstants; import org.wso2.carbon.user.core.UserStoreClientException; import org.wso2.carbon.user.core.common.AbstractUserStoreManager; -import org.wso2.carbon.user.core.util.UserCoreUtil; import java.util.ArrayList; import java.util.Arrays; From a0fc4c3dfb7188c74146ce5abb2db7a543b2e4d8 Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Sun, 26 Jan 2025 21:13:12 +0530 Subject: [PATCH 12/23] update runtime resolving logic for discoverable apps --- .../application/mgt/ApplicationConstants.java | 2 + .../mgt/dao/impl/ApplicationDAOImpl.java | 98 +++++++++++++--- .../mgt/dao/impl/ApplicationMgtDBQueries.java | 108 +++++++++++------- 3 files changed, 150 insertions(+), 58 deletions(-) diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java index d8f15b6ca5d7..b0f702afc30d 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationConstants.java @@ -233,6 +233,8 @@ public static class ApplicationTableColumns { public static final String AUTHORIZATION_DETAILS_NAME = "AUTHORIZATION_DETAILS_NAME"; public static final String AUTHORIZATION_DETAILS_SCHEMA = "AUTHORIZATION_DETAILS_SCHEMA"; + public static final String GROUP_ID = "GROUP_ID"; + private ApplicationTableColumns() { } diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java index fa44f68f37fb..054358c0328d 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java @@ -107,6 +107,7 @@ import org.wso2.carbon.identity.secret.mgt.core.model.Secret; import org.wso2.carbon.user.api.Tenant; import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.user.core.UserStoreClientException; import org.wso2.carbon.user.core.common.AbstractUserStoreManager; import org.wso2.carbon.user.core.util.UserCoreUtil; import org.wso2.carbon.utils.DBUtils; @@ -5997,8 +5998,11 @@ public int getCountOfDiscoverableApplications(String filter, String tenantDomain statement.setString(ApplicationTableColumns.APP_NAME, filterResolvedForSQL); try (ResultSet resultSet = statement.executeQuery()) { - if (resultSet.next()) { - count = resultSet.getInt(1); + while (resultSet.next()) { + String groupId = resultSet.getString(ApplicationTableColumns.GROUP_ID); + if (groupId == null || checkLoggedInUserIsInGroup(groupId)) { + count++; + } } } } @@ -6023,14 +6027,18 @@ public ApplicationBasicInfo getDiscoverableApplicationBasicInfoByResourceId(Stri boolean isDiscoverable = false; try (Connection connection = IdentityDatabaseUtil.getDBConnection(false)) { try (NamedPreparedStatement statement = new NamedPreparedStatement(connection, - ApplicationMgtDBQueries.LOAD_APP_BY_TENANT_AND_UUID)) { + ApplicationMgtDBQueries.LOAD_DISCOVERABLE_APP_BY_TENANT_AND_UUID)) { statement.setInt(ApplicationTableColumns.TENANT_ID, IdentityTenantUtil.getTenantId(tenantDomain)); statement.setString(ApplicationTableColumns.UUID, resourceId); try (ResultSet resultSet = statement.executeQuery()) { while (resultSet.next()) { - applicationBasicInfo = buildApplicationBasicInfo(resultSet); - isDiscoverable = getBooleanValue(resultSet.getString(ApplicationTableColumns.IS_DISCOVERABLE)); + String groupId = resultSet.getString(ApplicationTableColumns.GROUP_ID); + if (checkLoggedInUserIsInGroup(groupId)) { + applicationBasicInfo = buildApplicationBasicInfo(resultSet); + isDiscoverable = + getBooleanValue(resultSet.getString(ApplicationTableColumns.IS_DISCOVERABLE)); + } } } } @@ -6052,7 +6060,7 @@ public ApplicationBasicInfo getDiscoverableApplicationBasicInfoByResourceId(Stri public boolean isApplicationDiscoverable(String resourceId, String tenantDomain) throws IdentityApplicationManagementException { - int count = 0; + boolean isDiscoverable = false; try (Connection connection = IdentityDatabaseUtil.getDBConnection(false)) { try (NamedPreparedStatement statement = new NamedPreparedStatement(connection, ApplicationMgtDBQueries.IS_APP_BY_TENANT_AND_UUID_DISCOVERABLE)) { @@ -6061,7 +6069,10 @@ public boolean isApplicationDiscoverable(String resourceId, String tenantDomain) try (ResultSet resultSet = statement.executeQuery()) { if (resultSet.next()) { - count = resultSet.getInt(1); + String groupId = resultSet.getString(ApplicationTableColumns.GROUP_ID); + if (groupId == null || checkLoggedInUserIsInGroup(groupId)) { + isDiscoverable = true; + } } } } @@ -6069,7 +6080,7 @@ public boolean isApplicationDiscoverable(String resourceId, String tenantDomain) throw new IdentityApplicationManagementServerException("Error while getting discoverable application " + "basic information for resourceId: " + resourceId + " in tenantDomain: " + tenantDomain, e); } - return count > 0; + return isDiscoverable; } @Override @@ -6266,7 +6277,7 @@ private String getSPPropertyValueByPropertyKey(int applicationId, String propert private List getDiscoverableApplicationBasicInfo(int limit, int offset, String tenantDomain) throws IdentityApplicationManagementException { - List applicationBasicInfoList = new ArrayList<>(); + HashMap applicationBasicInfos = new HashMap<>(); try (Connection connection = IdentityDatabaseUtil.getDBConnection(false)) { String databaseVendorType = connection.getMetaData().getDatabaseProductName(); @@ -6283,7 +6294,7 @@ private List getDiscoverableApplicationBasicInfo(int limit try (ResultSet resultSet = statement.executeQuery()) { while (resultSet.next()) { - applicationBasicInfoList.add(buildApplicationBasicInfo(resultSet)); + buildDiscoverableAppBasicInfo(applicationBasicInfos, resultSet); } } } @@ -6292,12 +6303,67 @@ private List getDiscoverableApplicationBasicInfo(int limit " for discoverable applications in tenantDomain: " + tenantDomain, e); } - return Collections.unmodifiableList(applicationBasicInfoList); + return Collections.unmodifiableList(new ArrayList<>(applicationBasicInfos.values())); + } + + /** + * Build the discoverable application basic information from the result set. + * + * @param applicationBasicInfos HashMap to store the application basic information. + * @param resultSet Current result set. + * @throws SQLException Error while reading the result set. + * @throws IdentityApplicationManagementException Error while building the application basic information. + */ + private void buildDiscoverableAppBasicInfo(HashMap applicationBasicInfos, + ResultSet resultSet) + throws SQLException, IdentityApplicationManagementException { + + int applicationId = resultSet.getInt(ApplicationTableColumns.ID); + if (!applicationBasicInfos.containsKey(applicationId)) { + String groupId = resultSet.getString(ApplicationTableColumns.GROUP_ID); + if (groupId == null) { + applicationBasicInfos.put(applicationId, buildApplicationBasicInfo(resultSet)); + return; + } + if (checkLoggedInUserIsInGroup(groupId)) { + applicationBasicInfos.put(applicationId, buildApplicationBasicInfo(resultSet)); + } + } + } + + /** + * Check whether the logged-in user is in the provided group. + * + * @param groupId Group id to check whether the user is in. + * @return True if the user is in the group. + * @throws IdentityApplicationManagementException Error while checking the user is in the group. + */ + private boolean checkLoggedInUserIsInGroup(String groupId) throws IdentityApplicationManagementException { + + String loggedInUserId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUserId(); + String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); + AbstractUserStoreManager userStoreManager = ApplicationMgtUtil.getUserStoreManager(tenantDomain); + try { + if (userStoreManager.isUserInGroup(loggedInUserId, groupId)) { + return true; + } + } catch (org.wso2.carbon.user.core.UserStoreException e) { + String msg = "Error while checking the user: " + loggedInUserId + " is in the group: " + groupId + + " in tenant: " + tenantDomain; + if (e instanceof UserStoreClientException) { + if (log.isDebugEnabled()) { + log.debug(msg, e); + } + } else { + throw new IdentityApplicationManagementException(msg, e); + } + } + return false; } private int getCountOfDiscoverableApplications(String tenantDomain) throws IdentityApplicationManagementException { - int count; + int count = 0; try (Connection connection = IdentityDatabaseUtil.getDBConnection(false)) { try (NamedPreparedStatement statement = @@ -6306,8 +6372,12 @@ private int getCountOfDiscoverableApplications(String tenantDomain) throws Ident statement.setInt(ApplicationTableColumns.TENANT_ID, IdentityTenantUtil.getTenantId(tenantDomain)); try (ResultSet resultSet = statement.executeQuery()) { - resultSet.next(); - count = resultSet.getInt(1); + while (resultSet.next()) { + String groupId = resultSet.getString(ApplicationTableColumns.GROUP_ID); + if (groupId == null || checkLoggedInUserIsInGroup(groupId)) { + count++; + } + } } } } catch (SQLException e) { diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationMgtDBQueries.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationMgtDBQueries.java index d161d352ff1f..35d4c9cbdef7 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationMgtDBQueries.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationMgtDBQueries.java @@ -436,79 +436,99 @@ public class ApplicationMgtDBQueries { "WHERE TENANT_ID = :TENANT_ID; AND APP_NAME = :APP_NAME;"; // DB queries to manage application discoverability - public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_MYSQL = "SELECT ID, APP_NAME, VERSION, " + - "DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID FROM SP_APP WHERE " + + public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_MYSQL = "SELECT SP_APP.ID, APP_NAME, VERSION, " + + "DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID, GROUP_ID FROM SP_APP " + + "LEFT JOIN APP_GROUP_ASSOCIATION ON SP_APP.ID = APP_GROUP_ASSOCIATION.APP_ID WHERE " + "TENANT_ID = :TENANT_ID; AND IS_DISCOVERABLE = '1' ORDER BY ID DESC LIMIT :OFFSET;, :LIMIT;"; public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_ORACLE = "SELECT ID, APP_NAME, VERSION, " + - "DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID FROM " + + "DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID, GROUP_ID FROM " + "(SELECT ID, APP_NAME, VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID" + - ", rownum AS rnum FROM " + - "(SELECT ID, APP_NAME, VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, " + - "TENANT_ID, IS_DISCOVERABLE FROM SP_APP ORDER BY ID DESC) WHERE TENANT_ID = :TENANT_ID; AND " + + ", GROUP_ID, rownum AS rnum FROM " + + "(SELECT SP_APP.ID, APP_NAME, VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, " + + "TENANT_ID, IS_DISCOVERABLE, GROUP_ID FROM SP_APP LEFT OUTER JOIN APP_GROUP_ASSOCIATION ON SP_APP.ID = " + + "APP_GROUP_ASSOCIATION.APP_ID ORDER BY ID DESC) WHERE TENANT_ID = :TENANT_ID; AND " + "rownum <= :END_INDEX; AND IS_DISCOVERABLE = '1') WHERE rnum > :ZERO_BASED_START_INDEX;"; - public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_MSSQL = "SELECT ID, APP_NAME, VERSION, " + - "DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID FROM " + - "SP_APP WHERE TENANT_ID = :TENANT_ID; AND IS_DISCOVERABLE = '1' ORDER BY ID " + + public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_MSSQL = "SELECT SP_APP.ID, APP_NAME, VERSION, " + + "DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID, GROUP_ID FROM " + + "SP_APP LEFT OUTER JOIN APP_GROUP_ASSOCIATION ON SP_APP.ID = APP_GROUP_ASSOCIATION.APP_ID " + + "WHERE TENANT_ID = :TENANT_ID; AND IS_DISCOVERABLE = '1' ORDER BY ID " + "DESC OFFSET :OFFSET; ROWS FETCH NEXT :LIMIT; ROWS ONLY"; - public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_POSTGRESQL = "SELECT ID, " + - "APP_NAME, VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID FROM " + - "SP_APP WHERE TENANT_ID = :TENANT_ID; AND IS_DISCOVERABLE = '1' ORDER BY ID " + - "DESC LIMIT :LIMIT; OFFSET :OFFSET;"; + public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_POSTGRESQL = "SELECT SP_APP.ID, " + + "APP_NAME, VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID, " + + "GROUP_ID FROM SP_APP LEFT JOIN APP_GROUP_ASSOCIATION ON SP_APP.ID = APP_GROUP_ASSOCIATION.APP_ID " + + "WHERE TENANT_ID = :TENANT_ID; AND IS_DISCOVERABLE = '1' ORDER BY ID DESC LIMIT :LIMIT; OFFSET :OFFSET;"; public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_DB2SQL = "SELECT ID, " + - "APP_NAME, VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID FROM " - + "(SELECT ROW_NUMBER() OVER(ORDER BY ID DESC) AS rn,SP_APP.* FROM SP_APP WHERE TENANT_ID = :TENANT_ID; " + - "AND IS_DISCOVERABLE = '1') WHERE rn BETWEEN :ONE_BASED_START_INDEX; AND :END_INDEX;"; + "APP_NAME, VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID, " + + "GROUP_ID FROM (SELECT ROW_NUMBER() OVER(ORDER BY SP_APP.ID DESC) AS rn, SP_APP.*, GROUP_ID FROM " + + "SP_APP LEFT OUTER JOIN APP_GROUP_ASSOCIATION ON SP_APP.ID = APP_GROUP_ASSOCIATION.APP_ID WHERE " + + "TENANT_ID = :TENANT_ID; AND IS_DISCOVERABLE = '1') WHERE rn BETWEEN :ONE_BASED_START_INDEX; AND " + + ":END_INDEX;"; public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_INFORMIX = "SELECT SKIP :OFFSET; FIRST " + - ":LIMIT; ID, APP_NAME, VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, " + - "TENANT_ID FROM SP_APP WHERE TENANT_ID = :TENANT_ID; AND IS_DISCOVERABLE = '1' ORDER BY ID DESC"; + ":LIMIT; SP_APP.ID, APP_NAME, VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, " + + "TENANT_ID, GROUP_ID FROM SP_APP LEFT OUTER JOIN APP_GROUP_ASSOCIATION ON SP_APP.ID = " + + "APP_GROUP_ASSOCIATION.APP_ID WHERE TENANT_ID = :TENANT_ID; AND IS_DISCOVERABLE = '1' ORDER BY ID DESC"; public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_AND_APP_NAME_MYSQL = - "SELECT ID, APP_NAME, VERSION, DESCRIPTION" + - " , UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID FROM SP_APP " + - "WHERE TENANT_ID = :TENANT_ID; " + - "AND APP_NAME LIKE :APP_NAME; AND IS_DISCOVERABLE = '1' ORDER BY ID DESC LIMIT :OFFSET;, :LIMIT;"; + "SELECT SP_APP.ID, APP_NAME, VERSION, DESCRIPTION" + + " , UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID, GROUP_ID FROM SP_APP " + + "LEFT JOIN APP_GROUP_ASSOCIATION ON SP_APP.ID = APP_GROUP_ASSOCIATION.APP_ID WHERE TENANT_ID = " + + ":TENANT_ID; AND APP_NAME LIKE :APP_NAME; AND IS_DISCOVERABLE = '1' ORDER BY ID DESC LIMIT " + + ":OFFSET;, :LIMIT;"; public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_AND_APP_NAME_ORACLE = "SELECT ID, APP_NAME, " + - "VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID FROM " + - "(SELECT ID, APP_NAME, VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, " + - "TENANT_ID, rownum AS rnum FROM " + + "VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID, GROUP_ID FROM " + "(SELECT ID, APP_NAME, VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, " + - "TENANT_ID FROM SP_APP ORDER BY ID DESC) WHERE TENANT_ID = :TENANT_ID; AND APP_NAME LIKE :APP_NAME; AND " + - "rownum <= :END_INDEX; AND IS_DISCOVERABLE = '1') WHERE rnum > :ZERO_BASED_START_INDEX;"; - - public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_AND_APP_NAME_MSSQL = "SELECT ID, APP_NAME, " + - "VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID FROM " + - "SP_APP WHERE TENANT_ID = :TENANT_ID; AND APP_NAME LIKE :APP_NAME; AND IS_DISCOVERABLE = '1' ORDER BY ID " + + "TENANT_ID, GROUP_ID, rownum AS rnum FROM " + + "(SELECT SP_APP.ID, APP_NAME, VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, " + + "TENANT_ID, GROUP_ID FROM SP_APP LEFT OUTER JOIN APP_GROUP_ASSOCIATION ON SP_APP.ID = " + + "APP_GROUP_ASSOCIATION.APP_ID ORDER BY ID DESC) WHERE TENANT_ID = :TENANT_ID; AND APP_NAME LIKE " + + ":APP_NAME; AND rownum <= :END_INDEX; AND IS_DISCOVERABLE = '1') WHERE rnum > :ZERO_BASED_START_INDEX;"; + + public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_AND_APP_NAME_MSSQL = "SELECT SP_APP.ID, APP_NAME, " + + "VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID, GROUP_ID FROM " + + "SP_APP LEFT OUTER JOIN APP_GROUP_ASSOCIATION ON SP_APP.ID = APP_GROUP_ASSOCIATION.APP_ID WHERE " + + "TENANT_ID = :TENANT_ID; AND APP_NAME LIKE :APP_NAME; AND IS_DISCOVERABLE = '1' ORDER BY ID " + "DESC OFFSET :OFFSET; ROWS FETCH NEXT :LIMIT; ROWS ONLY"; - public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_AND_APP_NAME_POSTGRESQL = "SELECT ID, " + + public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_AND_APP_NAME_POSTGRESQL = "SELECT SP_APP.ID, " + "APP_NAME, VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID FROM " + - "SP_APP WHERE TENANT_ID = :TENANT_ID; AND APP_NAME LIKE :APP_NAME; AND IS_DISCOVERABLE = '1' ORDER BY ID " + + "SP_APP LEFT JOIN APP_GROUP_ASSOCIATION ON SP_APP.ID = APP_GROUP_ASSOCIATION.APP_ID WHERE TENANT_ID = " + + ":TENANT_ID; AND APP_NAME LIKE :APP_NAME; AND IS_DISCOVERABLE = '1' ORDER BY ID " + "DESC LIMIT :LIMIT; OFFSET :OFFSET;"; - public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_AND_APP_NAME_DB2 = "SELECT ID, " + - "APP_NAME, VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID FROM " - + "(SELECT ROW_NUMBER() OVER(ORDER BY ID DESC) AS rn,SP_APP.* FROM SP_APP WHERE TENANT_ID = :TENANT_ID; " + - "AND APP_NAME LIKE :APP_NAME; AND IS_DISCOVERABLE = '1)WHERE rn BETWEEN :ONE_BASED_START_INDEX; AND " + + public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_AND_APP_NAME_DB2 = "SELECT ID, APP_NAME, " + + "VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, TENANT_ID, GROUP_ID FROM " + + "(SELECT ROW_NUMBER() OVER(ORDER BY SP_APP.ID DESC) AS rn, SP_APP.*, GROUP_ID FROM SP_APP LEFT OUTER " + + "JOIN APP_GROUP_ASSOCIATION ON SP_APP.ID = APP_GROUP_ASSOCIATION.APP_ID WHERE TENANT_ID = :TENANT_ID; " + + "AND APP_NAME LIKE :APP_NAME; AND IS_DISCOVERABLE = '1') WHERE rn BETWEEN :ONE_BASED_START_INDEX; AND " + ":END_INDEX;"; public static final String LOAD_DISCOVERABLE_APPS_BY_TENANT_AND_APP_NAME_INFORMIX = "SELECT SKIP :OFFSET; FIRST " + - ":LIMIT; ID, APP_NAME, VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, " + - "TENANT_ID FROM SP_APP WHERE TENANT_ID = :TENANT_ID; AND APP_NAME LIKE :APP_NAME; AND " + + ":LIMIT; SP_APP.ID, APP_NAME, VERSION, DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, USERNAME, USER_STORE, " + + "TENANT_ID, GROUP_ID FROM SP_APP LEFT OUTER JOIN APP_GROUP_ASSOCIATION ON SP_APP.ID = " + + "APP_GROUP_ASSOCIATION.APP_ID WHERE TENANT_ID = :TENANT_ID; AND APP_NAME LIKE :APP_NAME; AND " + "IS_DISCOVERABLE = '1' ORDER BY ID DESC"; - public static final String LOAD_DISCOVERABLE_APP_COUNT_BY_APP_NAME_AND_TENANT = "SELECT COUNT(UUID) FROM SP_APP " + - "WHERE TENANT_ID = :TENANT_ID; AND APP_NAME LIKE :APP_NAME; AND IS_DISCOVERABLE = '1'"; + public static final String LOAD_DISCOVERABLE_APP_BY_TENANT_AND_UUID = "SELECT SP_APP.ID, APP_NAME, VERSION, " + + "DESCRIPTION, UUID, IMAGE_URL, ACCESS_URL, IS_DISCOVERABLE, USERNAME, USER_STORE, TENANT_ID, GROUP_ID " + + "FROM SP_APP LEFT OUTER JOIN APP_GROUP_ASSOCIATION ON SP_APP.ID = APP_GROUP_ASSOCIATION.APP_ID WHERE " + + "TENANT_ID = :TENANT_ID; AND UUID = :UUID;"; + + public static final String LOAD_DISCOVERABLE_APP_COUNT_BY_APP_NAME_AND_TENANT = "SELECT GROUP_ID FROM SP_APP " + + "LEFT OUTER JOIN APP_GROUP_ASSOCIATION ON SP_APP.ID = APP_GROUP_ASSOCIATION.APP_ID WHERE TENANT_ID = " + + ":TENANT_ID; AND APP_NAME LIKE :APP_NAME; AND IS_DISCOVERABLE = '1'"; - public static final String LOAD_DISCOVERABLE_APP_COUNT_BY_TENANT = "SELECT COUNT(UUID) FROM SP_APP " + - "WHERE TENANT_ID = :TENANT_ID; AND IS_DISCOVERABLE = '1'"; + public static final String LOAD_DISCOVERABLE_APP_COUNT_BY_TENANT = "SELECT GROUP_ID FROM SP_APP LEFT OUTER " + + "JOIN APP_GROUP_ASSOCIATION ON SP_APP.ID = APP_GROUP_ASSOCIATION.APP_ID WHERE TENANT_ID = :TENANT_ID; " + + "AND IS_DISCOVERABLE = '1'"; - public static final String IS_APP_BY_TENANT_AND_UUID_DISCOVERABLE = "SELECT COUNT(UUID) FROM SP_APP WHERE " + + public static final String IS_APP_BY_TENANT_AND_UUID_DISCOVERABLE = "SELECT GROUP_ID FROM SP_APP LEFT OUTER " + + "JOIN APP_GROUP_ASSOCIATION ON SP_APP.ID = APP_GROUP_ASSOCIATION.APP_ID WHERE " + "TENANT_ID = :TENANT_ID; AND UUID = :UUID; AND IS_DISCOVERABLE = '1'"; public static final String GET_TOTAL_SP_CLAIM_USAGES = "SELECT COUNT(*) FROM SP_CLAIM_MAPPING WHERE TENANT_ID = ?" + From ac6aa3906cfdf0af54da6503c9fd174bdad3f717 Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Mon, 27 Jan 2025 15:05:10 +0530 Subject: [PATCH 13/23] update runtime resolving logic for discoverable apps in filtering service --- .../application/mgt/dao/impl/ApplicationDAOImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java index 054358c0328d..650938bdbcb5 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImpl.java @@ -5944,7 +5944,7 @@ public List getDiscoverableApplicationBasicInfo(int limit, String filterResolvedForSQL = resolveSQLFilter(filter); - List applicationBasicInfoList = new ArrayList<>(); + HashMap applicationBasicInfos = new HashMap<>(); try (Connection connection = IdentityDatabaseUtil.getDBConnection(false)) { String databaseVendorType = connection.getMetaData().getDatabaseProductName(); @@ -5962,7 +5962,7 @@ public List getDiscoverableApplicationBasicInfo(int limit, try (ResultSet resultSet = statement.executeQuery()) { while (resultSet.next()) { - applicationBasicInfoList.add(buildApplicationBasicInfo(resultSet)); + buildDiscoverableAppBasicInfo(applicationBasicInfos, resultSet); } } } @@ -5971,7 +5971,7 @@ public List getDiscoverableApplicationBasicInfo(int limit, " for discoverable applications in tenantDomain: " + tenantDomain, e); } - return Collections.unmodifiableList(applicationBasicInfoList); + return Collections.unmodifiableList(new ArrayList<>(applicationBasicInfos.values())); } @Override From 4d308de6ecba58815d2d3a5003e5c5d4121e2a20 Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Tue, 28 Jan 2025 15:33:59 +0530 Subject: [PATCH 14/23] add unit tests for getGroups service method --- .../mgt/ApplicationManagementServiceImpl.java | 2 +- .../ApplicationManagementServiceImplTest.java | 122 ++++++++++++++++++ 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java index eb8e37d0711b..1e5b09a6707c 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImpl.java @@ -3314,7 +3314,7 @@ public List getGroups(String tenantDomain, String domainName, No groupBasicInfos.add(groupBasicInfo); } return groupBasicInfos; - } catch (UserStoreException e) { + } catch (org.wso2.carbon.user.core.UserStoreException e) { throw new IdentityApplicationManagementServerException(ERROR_RETRIEVING_GROUP_LIST.getCode(), String.format(ERROR_RETRIEVING_GROUP_LIST.getDescription(), domainName), e); } diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImplTest.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImplTest.java index 46c556a07b83..0f0b1de0541b 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImplTest.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImplTest.java @@ -19,12 +19,14 @@ package org.wso2.carbon.identity.application.mgt; import org.apache.commons.lang.StringUtils; +import org.mockito.ArgumentCaptor; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; +import org.testng.annotations.Ignore; import org.testng.annotations.Test; import org.wso2.carbon.CarbonConstants; import org.wso2.carbon.base.CarbonBaseConstants; @@ -44,6 +46,7 @@ import org.wso2.carbon.identity.application.common.model.ClaimMapping; import org.wso2.carbon.identity.application.common.model.ClientAttestationMetaData; import org.wso2.carbon.identity.application.common.model.FederatedAuthenticatorConfig; +import org.wso2.carbon.identity.application.common.model.GroupBasicInfo; import org.wso2.carbon.identity.application.common.model.IdentityProvider; import org.wso2.carbon.identity.application.common.model.InboundAuthenticationConfig; import org.wso2.carbon.identity.application.common.model.InboundAuthenticationRequestConfig; @@ -78,6 +81,9 @@ import org.wso2.carbon.identity.common.testng.realm.InMemoryRealmService; import org.wso2.carbon.identity.common.testng.realm.MockUserStoreManager; import org.wso2.carbon.identity.core.internal.IdentityCoreServiceDataHolder; +import org.wso2.carbon.identity.core.model.ExpressionNode; +import org.wso2.carbon.identity.core.model.Node; +import org.wso2.carbon.identity.core.model.OperationNode; import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.core.util.IdentityUtil; @@ -104,6 +110,11 @@ import org.wso2.carbon.registry.core.session.UserRegistry; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.api.UserStoreManager; +import org.wso2.carbon.user.core.UserRealm; +import org.wso2.carbon.user.core.common.AbstractUserStoreManager; +import org.wso2.carbon.user.core.common.Group; +import org.wso2.carbon.user.core.model.Condition; +import org.wso2.carbon.user.core.model.ExpressionCondition; import org.wso2.carbon.user.core.service.RealmService; import java.lang.reflect.Field; @@ -122,20 +133,30 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; import static org.wso2.carbon.CarbonConstants.REGISTRY_SYSTEM_USERNAME; import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.TEMPLATE_ID_SP_PROPERTY_NAME; import static org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants.TEMPLATE_VERSION_SP_PROPERTY_NAME; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.ERROR_RETRIEVING_GROUP_LIST; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.INVALID_GROUP_FILTER; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.INVALID_USER_STORE_DOMAIN; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.FILTER_CO; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.IS_FRAGMENT_APP; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.NAME; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.PORTAL_NAMES_CONFIG_ELEMENT; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.TRUSTED_APP_CONSENT_REQUIRED_PROPERTY; import static org.wso2.carbon.identity.certificate.management.constant.CertificateMgtErrors.ERROR_INVALID_CERTIFICATE_CONTENT; +import static org.wso2.carbon.user.core.UserStoreConfigConstants.GROUP_NAME_ATTRIBUTE; import static org.wso2.carbon.utils.multitenancy.MultitenantConstants.SUPER_TENANT_DOMAIN_NAME; import static org.wso2.carbon.utils.multitenancy.MultitenantConstants.SUPER_TENANT_ID; @@ -190,6 +211,7 @@ public class ApplicationManagementServiceImplTest { private static final int L1_TENANT_ID = 1; private static final String L2_ORG_ID = "30b701c6-e309-4241-b047-0c299c45d1a0"; private static final int L2_TENANT_ID = 2; + private static final String DEFAULT_DOMAIN_NAME = "PRIMARY"; private IdPManagementDAO idPManagementDAO; private ApplicationManagementServiceImpl applicationManagementService; @@ -1838,6 +1860,106 @@ public void testServerExceptionsWhileRetrievingAncestorAppIds() throws Exception }); } + @DataProvider(name = "testGetGroupsDataProvider") + public Object[][] testGetGroupsDataProvider() { + + ExpressionNode validExpressionNode = new ExpressionNode(); + validExpressionNode.setOperation(FILTER_CO); + validExpressionNode.setAttributeValue(NAME); + validExpressionNode.setValue("test-value"); + ExpressionNode invalidExpressionNode1 = new ExpressionNode(); + invalidExpressionNode1.setAttributeValue("wrong-filter-attr"); + invalidExpressionNode1.setOperation(FILTER_CO); + ExpressionNode invalidExpressionNode2 = new ExpressionNode(); + invalidExpressionNode2.setAttributeValue(NAME); + invalidExpressionNode2.setOperation("wrong-filter-op"); + OperationNode operationNode = new OperationNode(FILTER_CO); + Group group1 = new Group("test-group-id-1", "test-group-name-1"); + Group group2 = new Group("test-group-id-2", "test-group-name-2"); + + return new Object[][] { + {ROOT_TENANT_DOMAIN, DEFAULT_DOMAIN_NAME, null, new ArrayList<>(Arrays.asList(group1, group2)), null, + null}, + {ROOT_TENANT_DOMAIN, null, null, new ArrayList<>(Arrays.asList(group1, group2)), null, null}, + {ROOT_TENANT_DOMAIN, DEFAULT_DOMAIN_NAME, operationNode, null, null, INVALID_GROUP_FILTER.getCode()}, + {ROOT_TENANT_DOMAIN, DEFAULT_DOMAIN_NAME, invalidExpressionNode1, null, null, + INVALID_GROUP_FILTER.getCode()}, + {ROOT_TENANT_DOMAIN, DEFAULT_DOMAIN_NAME, invalidExpressionNode2, null, null, + INVALID_GROUP_FILTER.getCode()}, + {ROOT_TENANT_DOMAIN, DEFAULT_DOMAIN_NAME, validExpressionNode, + new ArrayList<>(Arrays.asList(group1, group2)), null, null}, + {ROOT_TENANT_DOMAIN, "wrong-domain-name", validExpressionNode, null, null, + INVALID_USER_STORE_DOMAIN.getCode()}, + {ROOT_TENANT_DOMAIN, DEFAULT_DOMAIN_NAME, null, null, + new org.wso2.carbon.user.core.UserStoreException(), ERROR_RETRIEVING_GROUP_LIST.getCode()} + }; + } + + @Test(description = "Test the group listing functionality of the application management service.", + dataProvider = "testGetGroupsDataProvider") + public void testGetGroups(String tenantDomain, String domainName, Node filter, List userStoreGroupsResponse, + org.wso2.carbon.user.core.UserStoreException userStoreGroupsRequestException, + String expectedErrorCode) throws UserStoreException { + + AbstractUserStoreManager mockAbstractUserStoreManager = mock(AbstractUserStoreManager.class); + RealmService mockRealmService = mock(RealmService.class); + UserRealm mockUserRealmService = mock(UserRealm.class); + ApplicationManagementServiceComponentHolder mockApplicationManagementServiceComponentHolder = + mock(ApplicationManagementServiceComponentHolder.class); + try (MockedStatic applicationManagementServiceComponentHolder = + mockStatic(ApplicationManagementServiceComponentHolder.class)) { + applicationManagementServiceComponentHolder.when(ApplicationManagementServiceComponentHolder::getInstance) + .thenReturn(mockApplicationManagementServiceComponentHolder); + when(mockApplicationManagementServiceComponentHolder.getRealmService()).thenReturn(mockRealmService); + when(mockRealmService.getTenantUserRealm(eq(SUPER_TENANT_ID))).thenReturn(mockUserRealmService); + when(mockUserRealmService.getUserStoreManager()).thenReturn(mockAbstractUserStoreManager); + when(mockAbstractUserStoreManager.getSecondaryUserStoreManager(eq(DEFAULT_DOMAIN_NAME))).thenReturn( + mockAbstractUserStoreManager); + if (userStoreGroupsResponse != null) { + when(mockAbstractUserStoreManager.listGroups(nullable(Condition.class), nullable(String.class), + anyInt(), anyInt(), nullable(String.class), nullable(String.class))).thenReturn( + userStoreGroupsResponse); + } else if (userStoreGroupsRequestException != null) { + when(mockAbstractUserStoreManager.listGroups(nullable(Condition.class), nullable(String.class), + anyInt(), anyInt(), nullable(String.class), nullable(String.class))).thenThrow( + userStoreGroupsRequestException); + } + try { + List groups = applicationManagementService.getGroups(tenantDomain, domainName, filter); + for (int i = 0; i < groups.size(); i++) { + assertEquals(groups.get(i).getId(), userStoreGroupsResponse.get(i).getGroupID()); + assertEquals(groups.get(i).getName(), userStoreGroupsResponse.get(i).getGroupName()); + } + } catch (IdentityApplicationManagementException e) { + assertEquals(e.getErrorCode(), expectedErrorCode); + } + if (userStoreGroupsResponse != null || userStoreGroupsRequestException != null) { + ArgumentCaptor conditionArgumentCaptor = ArgumentCaptor.forClass(Condition.class); + ArgumentCaptor userDomainArgumentCaptor = ArgumentCaptor.forClass(String.class); + verify(mockAbstractUserStoreManager).listGroups(conditionArgumentCaptor.capture(), + userDomainArgumentCaptor.capture(), + eq(0), eq(0), nullable(String.class), nullable(String.class)); + if (filter == null) { + assertEquals(conditionArgumentCaptor.getValue().getOperation(), "SW"); + assertEquals(((ExpressionCondition) conditionArgumentCaptor.getValue()).getAttributeName(), + GROUP_NAME_ATTRIBUTE); + assertEquals(((ExpressionCondition) conditionArgumentCaptor.getValue()).getAttributeValue(), + StringUtils.EMPTY); + } else if (filter instanceof OperationNode) { + assertEquals(conditionArgumentCaptor.getValue().getOperation(), + conditionArgumentCaptor.getValue().getOperation()); + } else { + assertEquals(conditionArgumentCaptor.getValue().getOperation(), StringUtils.upperCase(FILTER_CO)); + assertEquals(((ExpressionCondition) conditionArgumentCaptor.getValue()).getAttributeName(), + GROUP_NAME_ATTRIBUTE); + assertEquals(((ExpressionCondition) conditionArgumentCaptor.getValue()).getAttributeValue(), + ((ExpressionNode) filter).getValue()); + } + assertEquals(userDomainArgumentCaptor.getValue(), DEFAULT_DOMAIN_NAME); + } + } + } + private void addApplicationConfigurations(ServiceProvider serviceProvider) { serviceProvider.setDescription("Created for testing"); From f26f27469e6e9d57969f263f6d8d37d6bc637e35 Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Tue, 28 Jan 2025 16:43:02 +0530 Subject: [PATCH 15/23] add unit tests for util method --- .../ApplicationManagementServiceImplTest.java | 1 - .../mgt/ApplicationMgtUtilTest.java | 70 +++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImplTest.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImplTest.java index 0f0b1de0541b..31ea5f439229 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImplTest.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationManagementServiceImplTest.java @@ -26,7 +26,6 @@ import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; -import org.testng.annotations.Ignore; import org.testng.annotations.Test; import org.wso2.carbon.CarbonConstants; import org.wso2.carbon.base.CarbonBaseConstants; diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtilTest.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtilTest.java index 6bb08527b3ef..6f2ea08dfa55 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtilTest.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtilTest.java @@ -20,6 +20,7 @@ import org.mockito.stubbing.Answer; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; +import org.testng.annotations.Ignore; import org.testng.annotations.Test; import org.wso2.carbon.base.CarbonBaseConstants; import org.wso2.carbon.base.ServerConfiguration; @@ -59,6 +60,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; @@ -66,11 +68,14 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertThrows; import static org.wso2.carbon.base.MultitenantConstants.SUPER_TENANT_ID; import static org.wso2.carbon.base.MultitenantConstants.TENANT_DOMAIN; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.DEFAULT_RESULTS_PER_PAGE; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ENABLE_APPLICATION_ROLE_VALIDATION_PROPERTY; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.ERROR_RETRIEVING_USERSTORE_MANAGER; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.UNSUPPORTED_USER_STORE_MANAGER; import static org.wso2.carbon.identity.application.mgt.ApplicationMgtUtil.PATH_CONSTANT; import static org.wso2.carbon.user.core.constants.UserCoreErrorConstants.ErrorMessages.ERROR_CODE_ROLE_ALREADY_EXISTS; import static org.wso2.carbon.utils.multitenancy.MultitenantConstants.SUPER_TENANT_DOMAIN_NAME; @@ -151,6 +156,7 @@ public Object[][] getAppNamesForDefaultRegex() { } @Test(dataProvider = "getAppNamesForDefaultRegex") + @Ignore public void testIsRegexValidated(String appName, boolean isValidName) { // Default app validation regex should allow names with alphanumeric, dot, space, underscore and hyphens. @@ -170,6 +176,7 @@ public Object[][] getAppNamesForCustomRegex() { } @Test(dataProvider = "getAppNamesForCustomRegex") + @Ignore public void testSpNameValidationWithCustomRegex(String appName, boolean isValidName) { final String customRegEx = "^[a-zA-Z0-9]+"; @@ -182,6 +189,7 @@ public void testSpNameValidationWithCustomRegex(String appName, boolean isValidN } @Test + @Ignore public void testBuildPermissions() { String[] permissions = new String[]{"permission"}; @@ -202,6 +210,7 @@ public Object[][] validateRolesDataProvider() { } @Test(dataProvider = "validateRolesDataProvider") + @Ignore public void testValidateRoles(String allowRoleValidationProperty, Boolean expected) { try (MockedStatic identityUtil = mockStatic(IdentityUtil.class)) { @@ -228,6 +237,7 @@ public Object[][] userAuthorizeDataProvider() { } @Test(dataProvider = "userAuthorizeDataProvider") + @Ignore public void testIsUserAuthorized(String applicationName, String userName, String allowRoleValidationProperty, String[] userRoles, int applicationId, Boolean expected) throws UserStoreException, IdentityApplicationManagementException { @@ -255,6 +265,7 @@ public void testIsUserAuthorized(String applicationName, String userName, String } @Test + @Ignore public void testIsUserAuthorizedUserStoreException() throws UserStoreException { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -268,6 +279,7 @@ public void testIsUserAuthorizedUserStoreException() throws UserStoreException { } @Test + @Ignore public void testCreateAppRole() throws UserStoreException, IdentityApplicationManagementException { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -279,6 +291,7 @@ public void testCreateAppRole() throws UserStoreException, IdentityApplicationMa } @Test + @Ignore public void testCreateAppRoleUserStoreException() throws UserStoreException { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -304,6 +317,7 @@ public void testCreateAppRoleUserStoreException() throws UserStoreException { } @Test + @Ignore public void testDeleteAppRole() throws UserStoreException, IdentityApplicationManagementException { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -315,6 +329,7 @@ public void testDeleteAppRole() throws UserStoreException, IdentityApplicationMa } @Test + @Ignore public void testDeleteAppRoleUserStoreException() throws UserStoreException { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -328,6 +343,7 @@ public void testDeleteAppRoleUserStoreException() throws UserStoreException { } @Test + @Ignore public void testRenameRole() throws UserStoreException { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -340,6 +356,7 @@ public void testRenameRole() throws UserStoreException { } @Test + @Ignore public void testRenameAppPermissionPathNode() throws IdentityApplicationManagementException, UserStoreException, RegistryException { @@ -370,6 +387,7 @@ public void testRenameAppPermissionPathNode() throws IdentityApplicationManageme } @Test + @Ignore public void testStorePermissions() throws Exception { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -419,6 +437,7 @@ public Object[][] updatePermissionDataProvider() { } @Test(dataProvider = "updatePermissionDataProvider") + @Ignore public void testUpdatePermission(String[] childPermissions, int childCount) throws IdentityApplicationManagementException, UserStoreException, RegistryException { @@ -475,6 +494,7 @@ private void loadPermissions(MockedStatic privilegedCar } @Test + @Ignore public void testDeletePermissions() throws RegistryException, IdentityApplicationManagementException { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -494,6 +514,7 @@ public void testDeletePermissions() throws RegistryException, IdentityApplicatio } @Test + @Ignore public void testDeletePermissionsRegistryException() throws RegistryException { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -516,6 +537,7 @@ public void testDeletePermissionsRegistryException() throws RegistryException { } @Test + @Ignore public void testConcatArrays() { Property[] properties1 = new Property[]{new Property()}; @@ -536,6 +558,7 @@ public Object[][] validApplicationOwnerDataProvider() { } @Test(dataProvider = "validApplicationOwnerDataProvider") + @Ignore public void testIsValidApplicationOwner(String username, String tenantDomain, Boolean hasOwner, Boolean expected) throws IdentityApplicationManagementException, UserStoreException { @@ -595,6 +618,7 @@ public Object[][] getItemsPerPageDataProvider() { } @Test(dataProvider = "getItemsPerPageDataProvider") + @Ignore public void testGetItemsPerPage(String itemsPerPagePropertyValue, int itemsPerPage) { try (MockedStatic serverConfiguration = mockStatic(ServerConfiguration.class)) { @@ -623,6 +647,7 @@ public Object[][] getApplicationUpdatedVersionDataProvider() { } @Test(dataProvider = "getApplicationUpdatedVersionDataProvider") + @Ignore public void testGetApplicationUpdatedVersion(String currentVersion, String authType, String expectedUpdatedVersion) { @@ -642,6 +667,51 @@ public void testGetApplicationUpdatedVersion(String currentVersion, String authT } + @DataProvider(name = "getUserStoreManagerDataProvider") + public Object[][] getUserStoreManagerDataProvider() { + + mockUserStoreManager = mock(UserStoreManager.class); + mockAbstractUserStoreManager = mock(AbstractUserStoreManager.class); + return new Object[][] { + {SUPER_TENANT_DOMAIN_NAME, mockAbstractUserStoreManager, null, null}, + {SUPER_TENANT_DOMAIN_NAME, null, new UserStoreException(), + ERROR_RETRIEVING_USERSTORE_MANAGER.getCode()}, + {SUPER_TENANT_DOMAIN_NAME, null, null, ERROR_RETRIEVING_USERSTORE_MANAGER.getCode()}, + {SUPER_TENANT_DOMAIN_NAME, mockUserStoreManager, null, UNSUPPORTED_USER_STORE_MANAGER.getCode()} + }; + } + + @Test(description = "Test getUserStoreManager method", dataProvider = "getUserStoreManagerDataProvider") + public void testGetUserStoreManager(String tenantDomain, UserStoreManager userStoreManager, + UserStoreException userStoreException, String expectedErrorCode) + throws UserStoreException { + + try (MockedStatic holderMockedStatic = mockStatic( + ApplicationManagementServiceComponentHolder.class); + MockedStatic identityUtilMockedStatic = mockStatic(IdentityTenantUtil.class)) { + mockUserRealm = mock(UserRealm.class); + mockRealmService = mock(RealmService.class); + holderMockedStatic.when(ApplicationManagementServiceComponentHolder::getInstance) + .thenReturn(mockApplicationManagementServiceComponentHolder); + identityUtilMockedStatic.when(() -> IdentityTenantUtil.getTenantId(eq(SUPER_TENANT_DOMAIN_NAME))) + .thenReturn(SUPER_TENANT_ID); + when(mockApplicationManagementServiceComponentHolder.getRealmService()).thenReturn(mockRealmService); + when(mockRealmService.getTenantUserRealm(eq(SUPER_TENANT_ID))).thenReturn(mockUserRealm); + if (userStoreException != null) { + when(mockUserRealm.getUserStoreManager()).thenThrow(userStoreException); + } else { + when(mockUserRealm.getUserStoreManager()).thenReturn(userStoreManager); + } + try { + AbstractUserStoreManager abstractUserStoreManager = + ApplicationMgtUtil.getUserStoreManager(tenantDomain); + assertNotNull(abstractUserStoreManager); + } catch (IdentityApplicationManagementException e) { + assertEquals(e.getErrorCode(), expectedErrorCode); + } + } + } + private void mockTenantRegistry(MockedStatic privilegedCarbonContext, MockedStatic carbonContext) { From e5aaf8d7f4108cb82fc57ad690cc022298f8fad1 Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Wed, 29 Jan 2025 18:30:50 +0530 Subject: [PATCH 16/23] add unit tests for application dao --- ...ationManagementServiceComponentHolder.java | 20 - .../mgt/dao/impl/ApplicationDAOImplTest.java | 396 ++++++++++++++++++ .../src/test/resources/dbscripts/identity.sql | 10 + .../src/test/resources/testng.xml | 1 + 4 files changed, 407 insertions(+), 20 deletions(-) create mode 100644 components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImplTest.java diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java index 8abcbdf71b62..256cf8edc98a 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java @@ -442,24 +442,4 @@ public void setApplicationCertificateMgtService( this.applicationCertificateMgtService = applicationCertificateMgtService; } - - /** - * Get UserStoreConfigService instance. - * - * @return UserStoreConfigService instance. - */ - public UserStoreConfigService getUserStoreConfigService() { - - return userStoreConfigService; - } - - /** - * Set UserStoreConfigService instance. - * - * @param userStoreConfigService UserStoreConfigService instance. - */ - public void setUserStoreConfigService(UserStoreConfigService userStoreConfigService) { - - this.userStoreConfigService = userStoreConfigService; - } } diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImplTest.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImplTest.java new file mode 100644 index 000000000000..bfe96c29e5da --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/dao/impl/ApplicationDAOImplTest.java @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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.wso2.carbon.identity.application.mgt.dao.impl; + +import org.mockito.MockedStatic; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import org.wso2.carbon.CarbonConstants; +import org.wso2.carbon.base.CarbonBaseConstants; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException; +import org.wso2.carbon.identity.application.common.model.ApplicationBasicInfo; +import org.wso2.carbon.identity.application.common.model.DiscoverableGroup; +import org.wso2.carbon.identity.application.common.model.GroupBasicInfo; +import org.wso2.carbon.identity.application.common.model.ServiceProvider; +import org.wso2.carbon.identity.application.mgt.dao.ApplicationDAO; +import org.wso2.carbon.identity.application.mgt.internal.ApplicationManagementServiceComponentHolder; +import org.wso2.carbon.identity.application.mgt.provider.ApplicationPermissionProvider; +import org.wso2.carbon.identity.common.testng.WithH2Database; +import org.wso2.carbon.identity.core.util.IdentityTenantUtil; +import org.wso2.carbon.identity.core.util.IdentityUtil; +import org.wso2.carbon.user.core.UserRealm; +import org.wso2.carbon.user.core.UserStoreClientException; +import org.wso2.carbon.user.core.UserStoreException; +import org.wso2.carbon.user.core.common.AbstractUserStoreManager; +import org.wso2.carbon.user.core.service.RealmService; + +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertThrows; +import static org.testng.Assert.assertTrue; +import static org.wso2.carbon.utils.multitenancy.MultitenantConstants.SUPER_TENANT_DOMAIN_NAME; +import static org.wso2.carbon.utils.multitenancy.MultitenantConstants.SUPER_TENANT_ID; + +/** + * Test class for ApplicationDAOImpl. + */ +@WithH2Database(jndiName = "jdbc/WSO2IdentityDB", files = {"dbscripts/identity.sql"}) +public class ApplicationDAOImplTest { + + private static final String DEFAULT_USER_STORE_DOMAIN = "PRIMARY"; + private static final String USERNAME = "test-user"; + private static final String USER_ID = "test-user-id"; + + MockedStatic mockIdentityTenantUtil; + MockedStatic mockIdentityUtil; + MockedStatic mockedApplicationManagementServiceComponentHolder; + ApplicationManagementServiceComponentHolder mockComponentHolder; + UserRealm mockUserRealm; + RealmService mockRealmService; + AbstractUserStoreManager mockAbstractUserStoreManager; + ApplicationPermissionProvider mockApplicationPermissionProvider; + + /** + * Setup the test environment for ApplicationDAOImpl. + */ + @BeforeClass + public void setup() throws org.wso2.carbon.user.api.UserStoreException, IdentityApplicationManagementException { + + mockIdentityTenantUtil = mockStatic(IdentityTenantUtil.class); + mockIdentityUtil = mockStatic(IdentityUtil.class); + mockedApplicationManagementServiceComponentHolder = + mockStatic(ApplicationManagementServiceComponentHolder.class); + mockComponentHolder = mock(ApplicationManagementServiceComponentHolder.class); + mockUserRealm = mock(UserRealm.class); + mockRealmService = mock(RealmService.class); + mockAbstractUserStoreManager = mock(AbstractUserStoreManager.class); + mockApplicationPermissionProvider = mock(ApplicationPermissionProvider.class); + setupInitConfigurations(); + } + + /** + * Clean up the test environment of ApplicationDAOImpl. + */ + @AfterClass + public void end() { + + mockIdentityTenantUtil.close(); + mockIdentityUtil.close(); + mockedApplicationManagementServiceComponentHolder.close(); + } + + @Test(description = "Update application without discoverable groups") + public void updateApplicationWithoutDiscoverableGroups() throws IdentityApplicationManagementException { + + ServiceProvider serviceProvider = new ServiceProvider(); + serviceProvider.setApplicationName("test-app"); + serviceProvider.setApplicationVersion("v1.0.0"); + ApplicationDAO applicationDAO = new ApplicationDAOImpl(); + int applicationId = applicationDAO.createApplication(serviceProvider, SUPER_TENANT_DOMAIN_NAME); + serviceProvider.setApplicationID(applicationId); + serviceProvider.setDiscoverable(true); + serviceProvider.setAccessUrl("https://localhost:5000/test-app"); + applicationDAO.updateApplication(serviceProvider, SUPER_TENANT_DOMAIN_NAME); + } + + @Test(description = "Update application with valid discoverable groups", dependsOnMethods = { + "updateApplicationWithoutDiscoverableGroups"}) + public void updateApplicationWithValidDiscoverableGroups() throws IdentityApplicationManagementException { + + ApplicationDAO applicationDAO = new ApplicationDAOImpl(); + ServiceProvider serviceProvider = applicationDAO.getApplication("test-app", SUPER_TENANT_DOMAIN_NAME); + serviceProvider.setDiscoverableGroups( + new DiscoverableGroup[] {getNewDiscoverableGroup(DEFAULT_USER_STORE_DOMAIN, 2, 0)}); + applicationDAO.updateApplication(serviceProvider, SUPER_TENANT_DOMAIN_NAME); + } + + @Test(description = "Update application by deleting existing discoverable groups", dependsOnMethods = { + "updateApplicationWithValidDiscoverableGroups"}) + public void updateApplicationByDeletingExistingDiscoverableGroups() + throws IdentityApplicationManagementException, UserStoreException { + + when(mockAbstractUserStoreManager.getGroupNameByGroupId(eq("test-group-id-0"))) + .thenReturn("test-group-name-0"); + when(mockAbstractUserStoreManager.getGroupNameByGroupId(eq("test-group-id-1"))) + .thenReturn("test-group-name-1"); + ApplicationDAO applicationDAO = new ApplicationDAOImpl(); + ServiceProvider serviceProvider = applicationDAO.getApplication("test-app", SUPER_TENANT_DOMAIN_NAME); + serviceProvider.setDiscoverableGroups( + new DiscoverableGroup[] {getNewDiscoverableGroup(DEFAULT_USER_STORE_DOMAIN, 2, 0), + getNewDiscoverableGroup("SECONDARY", 2, 0)}); + applicationDAO.updateApplication(serviceProvider, SUPER_TENANT_DOMAIN_NAME); + } + + @Test(description = "Update application with invalid discoverable groups", dependsOnMethods = { + "updateApplicationByDeletingExistingDiscoverableGroups"}) + public void updateApplicationWithInvalidDiscoverableGroups() + throws UserStoreException, IdentityApplicationManagementException { + + when(mockAbstractUserStoreManager.getGroupNameByGroupId(eq("test-group-id-0"))) + .thenReturn("test-group-name-0"); + when(mockAbstractUserStoreManager.getGroupNameByGroupId(eq("test-group-id-1"))) + .thenReturn("test-group-name-1"); + ApplicationDAO applicationDAO = new ApplicationDAOImpl(); + ServiceProvider serviceProvider = applicationDAO.getApplication("test-app", SUPER_TENANT_DOMAIN_NAME); + serviceProvider.setDiscoverableGroups( + new DiscoverableGroup[] {getNewDiscoverableGroup(DEFAULT_USER_STORE_DOMAIN, 2, 0), + getNewDiscoverableGroup(DEFAULT_USER_STORE_DOMAIN, 2, 0)}); + assertThrows(IdentityApplicationManagementException.class, + () -> applicationDAO.updateApplication(serviceProvider, SUPER_TENANT_DOMAIN_NAME)); + } + + @Test(description = "Test retrieving discoverable groups list when the mapped group ID does not exist", + dependsOnMethods = {"updateApplicationWithInvalidDiscoverableGroups"}) + public void testDiscoverableGroupsListWhenMappedGroupIdNotExist() + throws IdentityApplicationManagementException, UserStoreException { + + when(mockAbstractUserStoreManager.getGroupNameByGroupId(eq("test-group-id-0"))) + .thenReturn("test-group-name-0"); + when(mockAbstractUserStoreManager.getGroupNameByGroupId(eq("test-group-id-1"))) + .thenThrow(new UserStoreException()); + ApplicationDAO applicationDAO = new ApplicationDAOImpl(); + ServiceProvider serviceProvider = applicationDAO.getApplication("test-app", SUPER_TENANT_DOMAIN_NAME); + DiscoverableGroup[] discoverableGroups = serviceProvider.getDiscoverableGroups(); + String[] domainNames = new String[] {"SECONDARY", DEFAULT_USER_STORE_DOMAIN}; + for (int i = 0; i < discoverableGroups.length; i++) { + DiscoverableGroup discoverableGroup = discoverableGroups[i]; + assertEquals(discoverableGroup.getGroups().length, 1); + assertEquals(discoverableGroup.getGroups()[0].getName(), "test-group-name-0"); + assertEquals(discoverableGroup.getGroups()[0].getId(), "test-group-id-0"); + assertEquals(discoverableGroup.getUserStore(), domainNames[i]); + } + } + + @Test(description = "Test retrieving discoverable groups list when the mapped all group IDs do not exist", + dependsOnMethods = {"testDiscoverableGroupsListWhenMappedGroupIdNotExist"}) + public void testDiscoverableGroupsListWhenAllMappedGroupIdsNotExist() + throws IdentityApplicationManagementException, UserStoreException { + + when(mockAbstractUserStoreManager.getGroupNameByGroupId(eq("test-group-id-0"))) + .thenThrow(new UserStoreException()); + when(mockAbstractUserStoreManager.getGroupNameByGroupId(eq("test-group-id-1"))) + .thenThrow(new UserStoreException()); + ApplicationDAO applicationDAO = new ApplicationDAOImpl(); + ServiceProvider serviceProvider = applicationDAO.getApplication("test-app", SUPER_TENANT_DOMAIN_NAME); + assertNull(serviceProvider.getDiscoverableGroups()); + } + + @Test(description = "Test the correct discoverable apps list for logged in user", + dependsOnMethods = { "testDiscoverableGroupsListWhenAllMappedGroupIdsNotExist" }) + public void testDiscoverableAppsList() throws IdentityApplicationManagementException, UserStoreException { + + ApplicationDAO applicationDAO = new ApplicationDAOImpl(); + ServiceProvider serviceProvider1 = new ServiceProvider(); + serviceProvider1.setApplicationName("scl-app"); + serviceProvider1.setApplicationVersion("v1.0.0"); + serviceProvider1.setApplicationID(applicationDAO.createApplication(serviceProvider1, SUPER_TENANT_DOMAIN_NAME)); + serviceProvider1.setDiscoverable(true); + serviceProvider1.setAccessUrl("https://localhost:5000/scl-app"); + serviceProvider1.setDiscoverableGroups( + new DiscoverableGroup[] {getNewDiscoverableGroup(DEFAULT_USER_STORE_DOMAIN, 1, 0)}); + applicationDAO.updateApplication(serviceProvider1, SUPER_TENANT_DOMAIN_NAME); + ServiceProvider serviceProvider2 = new ServiceProvider(); + serviceProvider2.setApplicationName("medical-app"); + serviceProvider2.setApplicationVersion("v1.0.0"); + serviceProvider2.setApplicationID(applicationDAO.createApplication(serviceProvider2, SUPER_TENANT_DOMAIN_NAME)); + serviceProvider2.setDiscoverableGroups( + new DiscoverableGroup[] {getNewDiscoverableGroup(DEFAULT_USER_STORE_DOMAIN, 1, 1)}); + serviceProvider2.setDiscoverable(true); + serviceProvider2.setAccessUrl("https://localhost:5000/medical-app"); + applicationDAO.updateApplication(serviceProvider2, SUPER_TENANT_DOMAIN_NAME); + ServiceProvider serviceProvider3 = applicationDAO.getApplication("test-app", SUPER_TENANT_DOMAIN_NAME); + serviceProvider3.setDiscoverableGroups(null); + applicationDAO.updateApplication(serviceProvider3, SUPER_TENANT_DOMAIN_NAME); + when(mockAbstractUserStoreManager.isUserInGroup(eq(USER_ID), eq("test-group-id-0"))) + .thenReturn(true); + when(mockAbstractUserStoreManager.isUserInGroup(eq(USER_ID), eq("test-group-id-1"))) + .thenReturn(false); + List applicationBasicInfos = + applicationDAO.getDiscoverableApplicationBasicInfo(10, 0, null, null, null, SUPER_TENANT_DOMAIN_NAME); + assertEquals(applicationBasicInfos.size(), 2); + assertEquals(applicationBasicInfos.get(0).getApplicationName(), "test-app"); + assertEquals(applicationBasicInfos.get(1).getApplicationName(), "scl-app"); + } + + @Test(description = "Test retrieving discoverable apps when isUserInGroup throws an exception", + dependsOnMethods = { "testDiscoverableAppsList" }) + public void testDiscoverableAppsListWhenIsUserInGroupThrowsException() + throws IdentityApplicationManagementException, UserStoreException { + + ApplicationDAO applicationDAO = new ApplicationDAOImpl(); + when(mockAbstractUserStoreManager.isUserInGroup(eq(USER_ID), eq("test-group-id-0"))) + .thenReturn(true); + when(mockAbstractUserStoreManager.isUserInGroup(eq(USER_ID), eq("test-group-id-1"))) + .thenThrow(new UserStoreClientException()); + List applicationBasicInfos = + applicationDAO.getDiscoverableApplicationBasicInfo(10, 0, null, null, null, SUPER_TENANT_DOMAIN_NAME); + assertEquals(applicationBasicInfos.size(), 2); + assertEquals(applicationBasicInfos.get(0).getApplicationName(), "test-app"); + assertEquals(applicationBasicInfos.get(1).getApplicationName(), "scl-app"); + when(mockAbstractUserStoreManager.isUserInGroup(eq(USER_ID), eq("test-group-id-1"))) + .thenThrow(new UserStoreException()); + assertThrows(IdentityApplicationManagementException.class, + () -> applicationDAO.getDiscoverableApplicationBasicInfo(10, 0, null, null, null, + SUPER_TENANT_DOMAIN_NAME)); + } + + @Test(description = "Test retrieving discoverable apps list with a filter", + dependsOnMethods = {"testDiscoverableAppsListWhenIsUserInGroupThrowsException"}) + public void testDiscoverableAppsListWithFilter() + throws IdentityApplicationManagementException, UserStoreException { + + ApplicationDAO applicationDAO = new ApplicationDAOImpl(); + when(mockAbstractUserStoreManager.isUserInGroup(eq(USER_ID), eq("test-group-id-0"))) + .thenReturn(true); + when(mockAbstractUserStoreManager.isUserInGroup(eq(USER_ID), eq("test-group-id-1"))) + .thenReturn(true); + List applicationBasicInfos = + applicationDAO.getDiscoverableApplicationBasicInfo(10, 0, "medical*", null, null, + SUPER_TENANT_DOMAIN_NAME); + assertEquals(applicationBasicInfos.size(), 1); + assertEquals(applicationBasicInfos.get(0).getApplicationName(), "medical-app"); + } + + @Test(description = "Test retrieving discoverable apps using resource ID", dependsOnMethods = { + "testDiscoverableAppsListWithFilter" }) + public void testGetDiscoverableAppWithResourceId() + throws IdentityApplicationManagementException, UserStoreException { + + ApplicationDAO applicationDAO = new ApplicationDAOImpl(); + ServiceProvider serviceProvider = applicationDAO.getApplication("scl-app", SUPER_TENANT_DOMAIN_NAME); + when(mockAbstractUserStoreManager.isUserInGroup(eq(USER_ID), eq("test-group-id-0"))) + .thenReturn(true); + ApplicationBasicInfo applicationBasicInfo = applicationDAO.getDiscoverableApplicationBasicInfoByResourceId( + serviceProvider.getApplicationResourceId(), SUPER_TENANT_DOMAIN_NAME); + assertEquals(applicationBasicInfo.getApplicationName(), "scl-app"); + when(mockAbstractUserStoreManager.isUserInGroup(eq(USER_ID), eq("test-group-id-0"))) + .thenReturn(false); + assertNull(applicationDAO.getDiscoverableApplicationBasicInfoByResourceId( + serviceProvider.getApplicationResourceId(), SUPER_TENANT_DOMAIN_NAME)); + } + + @Test(description = "Test the isApplicationDiscoverable method", dependsOnMethods = { + "testGetDiscoverableAppWithResourceId" }) + public void testIsApplicationDiscoverable() + throws IdentityApplicationManagementException, UserStoreException { + + ApplicationDAO applicationDAO = new ApplicationDAOImpl(); + ServiceProvider serviceProvider = applicationDAO.getApplication("scl-app", SUPER_TENANT_DOMAIN_NAME); + when(mockAbstractUserStoreManager.isUserInGroup(eq(USER_ID), eq("test-group-id-0"))) + .thenReturn(true); + assertTrue(applicationDAO.isApplicationDiscoverable(serviceProvider.getApplicationResourceId(), + SUPER_TENANT_DOMAIN_NAME)); + when(mockAbstractUserStoreManager.isUserInGroup(eq(USER_ID), eq("test-group-id-0"))) + .thenReturn(false); + assertFalse(applicationDAO.isApplicationDiscoverable(serviceProvider.getApplicationResourceId(), + SUPER_TENANT_DOMAIN_NAME)); + serviceProvider = applicationDAO.getApplication("test-app", SUPER_TENANT_DOMAIN_NAME); + assertTrue(applicationDAO.isApplicationDiscoverable(serviceProvider.getApplicationResourceId(), + SUPER_TENANT_DOMAIN_NAME)); + } + + @Test(description = "Test getting count of discoverable applications", dependsOnMethods = { + "testIsApplicationDiscoverable" }) + public void testGetDiscoverableAppCount() throws UserStoreException, IdentityApplicationManagementException { + + when(mockAbstractUserStoreManager.isUserInGroup(eq(USER_ID), eq("test-group-id-0"))) + .thenReturn(true); + when(mockAbstractUserStoreManager.isUserInGroup(eq(USER_ID), eq("test-group-id-1"))) + .thenReturn(false); + ApplicationDAO applicationDAO = new ApplicationDAOImpl(); + assertEquals(applicationDAO.getCountOfDiscoverableApplications(null, SUPER_TENANT_DOMAIN_NAME), 2); + assertEquals(applicationDAO.getCountOfDiscoverableApplications("scl*", SUPER_TENANT_DOMAIN_NAME), 1); + } + + /** + * Get a new DiscoverableGroup object. + * + * @param userStore User store domain. + * @param numberOfGroups Number of groups to be added. + * @param startIndex Suffix start index of the group. + * @return New DiscoverableGroup object. + */ + private DiscoverableGroup getNewDiscoverableGroup(String userStore, int numberOfGroups, int startIndex) { + + DiscoverableGroup discoverableGroup = new DiscoverableGroup(); + discoverableGroup.setUserStore(userStore); + List groupBasicInfos = new ArrayList<>(); + for (int i = startIndex; i < numberOfGroups + startIndex; i++) { + GroupBasicInfo groupBasicInfo = new GroupBasicInfo(); + groupBasicInfo.setId("test-group-id-" + i); + groupBasicInfo.setName("test-group-name-" + i); + groupBasicInfos.add(groupBasicInfo); + } + discoverableGroup.setGroups(groupBasicInfos.toArray(new GroupBasicInfo[0])); + return discoverableGroup; + } + + /** + * Setup the configurations for the test. + */ + private void setupInitConfigurations() + throws org.wso2.carbon.user.api.UserStoreException, IdentityApplicationManagementException { + + String carbonHome = Paths.get(System.getProperty("user.dir"), "target", "test-classes", "repository"). + toString(); + System.setProperty(CarbonBaseConstants.CARBON_HOME, carbonHome); + System.setProperty(CarbonBaseConstants.CARBON_CONFIG_DIR_PATH, Paths.get(carbonHome, "conf").toString()); + + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(SUPER_TENANT_DOMAIN_NAME); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(SUPER_TENANT_ID); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(USERNAME); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setUserId(USER_ID); + + CarbonConstants.ENABLE_LEGACY_AUTHZ_RUNTIME = false; + + mockIdentityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(eq(SUPER_TENANT_DOMAIN_NAME))) + .thenReturn(SUPER_TENANT_ID); + mockIdentityTenantUtil.when(() -> IdentityTenantUtil.getTenantDomain(eq(SUPER_TENANT_ID))) + .thenReturn(SUPER_TENANT_DOMAIN_NAME); + + mockIdentityUtil.when(IdentityUtil::getIdentityConfigDirPath) + .thenReturn(Paths.get(carbonHome, "conf", "identity"). + toString()); + mockIdentityUtil.when(() -> IdentityUtil.extractDomainFromName(anyString())) + .thenReturn(DEFAULT_USER_STORE_DOMAIN); + mockIdentityUtil.when(IdentityUtil::getPrimaryDomainName).thenReturn(SUPER_TENANT_DOMAIN_NAME); + + mockedApplicationManagementServiceComponentHolder.when( + ApplicationManagementServiceComponentHolder::getInstance) + .thenReturn(mockComponentHolder); + when(mockComponentHolder.getRealmService()).thenReturn(mockRealmService); + when(mockRealmService.getTenantUserRealm(SUPER_TENANT_ID)).thenReturn(mockUserRealm); + when(mockUserRealm.getUserStoreManager()).thenReturn(mockAbstractUserStoreManager); + when(mockComponentHolder.getApplicationPermissionProvider()).thenReturn(mockApplicationPermissionProvider); + when(mockApplicationPermissionProvider.loadPermissions(anyString())).thenReturn(new ArrayList<>()); + } +} diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/resources/dbscripts/identity.sql b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/resources/dbscripts/identity.sql index 22204696d921..3a4a08b055ad 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/resources/dbscripts/identity.sql +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/resources/dbscripts/identity.sql @@ -1187,6 +1187,16 @@ CREATE TABLE IF NOT EXISTS AUTHORIZED_AUTHORIZATION_DETAILS_TYPES ( CONSTRAINT AUTHORIZED_AUTHORIZATION_DETAILS_UNIQUE UNIQUE (APP_ID, TYPE_ID) ); +CREATE TABLE IF NOT EXISTS APP_GROUP_ASSOCIATION ( + ID INTEGER NOT NULL AUTO_INCREMENT, + APP_ID INTEGER NOT NULL, + GROUP_ID VARCHAR (255) NOT NULL, + DOMAIN_NAME VARCHAR (255) NOT NULL, + PRIMARY KEY (ID), + FOREIGN KEY (APP_ID) REFERENCES SP_APP(ID) ON DELETE CASCADE, + CONSTRAINT APP_GROUP_DOMAIN_CONSTRAINT UNIQUE (APP_ID, GROUP_ID, DOMAIN_NAME) +); + -- --------------------------- INDEX CREATION ----------------------------- -- IDN_OAUTH2_ACCESS_TOKEN -- CREATE INDEX IDX_TC ON IDN_OAUTH2_ACCESS_TOKEN(TIME_CREATED); diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/resources/testng.xml b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/resources/testng.xml index 788f0b40425d..a73717f257d0 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/resources/testng.xml +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/resources/testng.xml @@ -25,6 +25,7 @@ + From a1bf69daac20e294137f8c3b5592d35b2a4cd526 Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Wed, 29 Jan 2025 23:35:31 +0530 Subject: [PATCH 17/23] add unit tests for application validator --- ...ationManagementServiceComponentHolder.java | 2 - .../mgt/ApplicationMgtUtilTest.java | 20 - .../mgt/DefaultApplicationValidatorTest.java | 330 --------- .../DefaultApplicationValidatorTest.java | 648 ++++++++++++++++++ .../src/test/resources/testng.xml | 2 +- 5 files changed, 649 insertions(+), 353 deletions(-) delete mode 100644 components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/DefaultApplicationValidatorTest.java create mode 100644 components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidatorTest.java diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java index 256cf8edc98a..289cde9c84f8 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/main/java/org/wso2/carbon/identity/application/mgt/internal/ApplicationManagementServiceComponentHolder.java @@ -32,7 +32,6 @@ import org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService; import org.wso2.carbon.identity.secret.mgt.core.SecretManager; import org.wso2.carbon.identity.secret.mgt.core.SecretResolveManager; -import org.wso2.carbon.identity.user.store.configuration.UserStoreConfigService; import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.utils.ConfigurationContextService; @@ -70,7 +69,6 @@ public class ApplicationManagementServiceComponentHolder { private ApplicationPermissionProvider applicationPermissionProvider; private APIResourceManager apiResourceManager; private RoleManagementService roleManagementServiceV2; - private UserStoreConfigService userStoreConfigService; private OrganizationManager organizationManager; private boolean isOrganizationManagementEnable = false; diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtilTest.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtilTest.java index 6f2ea08dfa55..1f2ea1315f07 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtilTest.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtilTest.java @@ -156,7 +156,6 @@ public Object[][] getAppNamesForDefaultRegex() { } @Test(dataProvider = "getAppNamesForDefaultRegex") - @Ignore public void testIsRegexValidated(String appName, boolean isValidName) { // Default app validation regex should allow names with alphanumeric, dot, space, underscore and hyphens. @@ -176,7 +175,6 @@ public Object[][] getAppNamesForCustomRegex() { } @Test(dataProvider = "getAppNamesForCustomRegex") - @Ignore public void testSpNameValidationWithCustomRegex(String appName, boolean isValidName) { final String customRegEx = "^[a-zA-Z0-9]+"; @@ -189,7 +187,6 @@ public void testSpNameValidationWithCustomRegex(String appName, boolean isValidN } @Test - @Ignore public void testBuildPermissions() { String[] permissions = new String[]{"permission"}; @@ -210,7 +207,6 @@ public Object[][] validateRolesDataProvider() { } @Test(dataProvider = "validateRolesDataProvider") - @Ignore public void testValidateRoles(String allowRoleValidationProperty, Boolean expected) { try (MockedStatic identityUtil = mockStatic(IdentityUtil.class)) { @@ -237,7 +233,6 @@ public Object[][] userAuthorizeDataProvider() { } @Test(dataProvider = "userAuthorizeDataProvider") - @Ignore public void testIsUserAuthorized(String applicationName, String userName, String allowRoleValidationProperty, String[] userRoles, int applicationId, Boolean expected) throws UserStoreException, IdentityApplicationManagementException { @@ -265,7 +260,6 @@ public void testIsUserAuthorized(String applicationName, String userName, String } @Test - @Ignore public void testIsUserAuthorizedUserStoreException() throws UserStoreException { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -279,7 +273,6 @@ public void testIsUserAuthorizedUserStoreException() throws UserStoreException { } @Test - @Ignore public void testCreateAppRole() throws UserStoreException, IdentityApplicationManagementException { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -291,7 +284,6 @@ public void testCreateAppRole() throws UserStoreException, IdentityApplicationMa } @Test - @Ignore public void testCreateAppRoleUserStoreException() throws UserStoreException { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -317,7 +309,6 @@ public void testCreateAppRoleUserStoreException() throws UserStoreException { } @Test - @Ignore public void testDeleteAppRole() throws UserStoreException, IdentityApplicationManagementException { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -329,7 +320,6 @@ public void testDeleteAppRole() throws UserStoreException, IdentityApplicationMa } @Test - @Ignore public void testDeleteAppRoleUserStoreException() throws UserStoreException { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -343,7 +333,6 @@ public void testDeleteAppRoleUserStoreException() throws UserStoreException { } @Test - @Ignore public void testRenameRole() throws UserStoreException { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -356,7 +345,6 @@ public void testRenameRole() throws UserStoreException { } @Test - @Ignore public void testRenameAppPermissionPathNode() throws IdentityApplicationManagementException, UserStoreException, RegistryException { @@ -387,7 +375,6 @@ public void testRenameAppPermissionPathNode() throws IdentityApplicationManageme } @Test - @Ignore public void testStorePermissions() throws Exception { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -437,7 +424,6 @@ public Object[][] updatePermissionDataProvider() { } @Test(dataProvider = "updatePermissionDataProvider") - @Ignore public void testUpdatePermission(String[] childPermissions, int childCount) throws IdentityApplicationManagementException, UserStoreException, RegistryException { @@ -494,7 +480,6 @@ private void loadPermissions(MockedStatic privilegedCar } @Test - @Ignore public void testDeletePermissions() throws RegistryException, IdentityApplicationManagementException { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -514,7 +499,6 @@ public void testDeletePermissions() throws RegistryException, IdentityApplicatio } @Test - @Ignore public void testDeletePermissionsRegistryException() throws RegistryException { try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -537,7 +521,6 @@ public void testDeletePermissionsRegistryException() throws RegistryException { } @Test - @Ignore public void testConcatArrays() { Property[] properties1 = new Property[]{new Property()}; @@ -558,7 +541,6 @@ public Object[][] validApplicationOwnerDataProvider() { } @Test(dataProvider = "validApplicationOwnerDataProvider") - @Ignore public void testIsValidApplicationOwner(String username, String tenantDomain, Boolean hasOwner, Boolean expected) throws IdentityApplicationManagementException, UserStoreException { @@ -618,7 +600,6 @@ public Object[][] getItemsPerPageDataProvider() { } @Test(dataProvider = "getItemsPerPageDataProvider") - @Ignore public void testGetItemsPerPage(String itemsPerPagePropertyValue, int itemsPerPage) { try (MockedStatic serverConfiguration = mockStatic(ServerConfiguration.class)) { @@ -647,7 +628,6 @@ public Object[][] getApplicationUpdatedVersionDataProvider() { } @Test(dataProvider = "getApplicationUpdatedVersionDataProvider") - @Ignore public void testGetApplicationUpdatedVersion(String currentVersion, String authType, String expectedUpdatedVersion) { diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/DefaultApplicationValidatorTest.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/DefaultApplicationValidatorTest.java deleted file mode 100644 index 3767fd3e9392..000000000000 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/DefaultApplicationValidatorTest.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * 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.wso2.carbon.identity.application.mgt; - -import org.apache.commons.lang.StringUtils; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.wso2.carbon.identity.application.common.model.InboundAuthenticationConfig; -import org.wso2.carbon.identity.application.common.model.InboundAuthenticationRequestConfig; -import org.wso2.carbon.identity.application.common.model.ServiceProvider; -import org.wso2.carbon.identity.application.common.model.SpTrustedAppMetadata; -import org.wso2.carbon.identity.application.common.model.script.AuthenticationScriptConfig; -import org.wso2.carbon.identity.application.mgt.validator.DefaultApplicationValidator; -import org.wso2.carbon.identity.core.util.IdentityUtil; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.TRUSTED_APP_CONSENT_REQUIRED_PROPERTY; - -/** - * Test class for DefaultApplicationValidator. - */ -public class DefaultApplicationValidatorTest { - - @DataProvider(name = "validateAdaptiveAuthScriptDataProvider") - public Object[][] validateAdaptiveAuthScriptDataProvider() { - - String scripOne = "var onLoginRequest = function(context) {\n" + - " \tfor (var step = 0; step < 5; step++) {\n" + - " \t\tLog.info('Walking east one step');\n" + - "\t} \n" + - " executeStep(1);\n" + - "};\n"; - String scripTwo = "var onLoginRequest = function(context) {for(var step = 0; step < 5; step++) {\n" + - " \t\tLog.info('Walking east one step');\n" + - "\t} \n" + - " executeStep(1);\n" + - "};\n"; - String scripThree = "var onLoginRequest = function(context) {for(var step = 0; step < 5; step++) {" + - "Log.info('Walking east one step');} executeStep(1);};"; - String scriptFour = "var onLoginRequest = function(context) {\n" + - " var n = 0;\n" + - "\tvar x = 0;\n" + - "\tvar abc = 'abc';while (n < 3) {\n" + - "\t n++;\n" + - "\t x += n;\n" + - "\t}\n" + - " executeStep(1);\n" + - "};\n"; - String scriptFive = "var onLoginRequest = function(context) {\n" + - "var i = 0;\n" + - "do {\n" + - " i += 1;\n" + - " console.log(i);\n" + - "}while(i < 5);\n" + - " executeStep(1);\n" + - "};\n"; - String scripSix = "var onLoginRequest = function(context) {\n" + - " var forceAuth = true;\n" + - " var therefore = true;\n" + - " var fivefor = true;\n" + - " var for_auth = true;\n" + - " var _for = true;\n" + - " var whilefor = true;\n" + - " var forwhile = true;\n" + - " var some_whiles = true;\n" + - " var fore = true;\n" + - " var foreach = true;\n" + - " var foreaches = true;\n" + - " executeStep(1);\n" + - "};\n"; - - String scriptSeven = "var onLoginRequest = function(context) {\n" + - " var array1 = ['a', 'b', 'c'];\n" + - " array1.forEach(element => console.log(element));\n" + - " executeStep(1);\n" + - "};\n"; - - String scriptEight = "// This script will only allow for login to application while the user's age is over " + - "configured value\n// The user will be redirected to an error page if the date of birth is not " + - "present or user is below configured var onLoginRequest = function(context) {for(var step = 0;" + - " step < 5; step++) {Log.info('Walking east one step');} executeStep(1);} value\n\nvar ageLimit" + - " = 18;\n\n// Error page to redirect " + - "unauthorized users,\n// can be either an absolute url or relative url to server root, or " + - "empty/null\n// null/empty value will redirect to the default error page\nvar errorPage = '';\n\n// " + - "Additional query params to be added to the above url.\n// Hint: Use i18n keys for error " + - "messages\nvar errorPageParameters = {\n 'status': 'Unauthorized',\n 'statusMsg': \"You need to " + - "be over \" + ageLimit + \" for login to this application.\"\n};\n\n/*\nHi am a multi line comment " + - "1\n*/\n// Date of birth attribute at the client side\nvar dateOfBirthClaim = 'http://wso2" + - ".org/claims/dob';\n\n// The validator function for DOB. Default validation check if the DOB is in " + - "YYYY-MM-dd format\nvar validateDOB = function (dob) {\n return dob.match(/^(\\d{4})-(\\d{2})-" + - "(\\d{2})$/);\n};\n\nvar onLoginRequest = function(context) {\n executeStep(1, {\n " + - "onSuccess: function (context) {\n var underAge = true;\n // Extracting user " + - "store domain of authenticated subject from the first step\n var dob = context" + - ".currentKnownSubject.localClaims[dateOfBirthClaim];// Adding a single line comment with for while " + - "foreach \n Log.debug('DOB for user ' + context.currentKnownSubject.identifier + ' is : " + - "' + dob);\n Log.debug('while for forEach ');\n if (dob && validateDOB(dob)) {\n var birthDate = new " + - "Date(dob);\n if (getAge(birthDate) >= ageLimit) {\n underAge = false;\n " + - " }\n }\n if (underAge === true) {\n Log.debug('User ' +" + - " context.currentKnownSubject.identifier + ' is under aged. Hence denied to login.');\n " + - " sendError(errorPage, errorPageParameters);\n }\n }\n });\n};\n/*\n* Hi am a " + - "multi line comment 2\n*/\nvar getAge = function(birthDate) {\n var /*Adding a multiline comment " + - "with for while foreach*/today = new Date();\n var age = today.getFullYear() - birthDate" + - ".getFullYear();\n var m = today.getMonth()/* Adding a multiline comment with for while foreach */" + - " - birthDate.getMonth();\n if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {\n " + - " age--;\n }\n return age;\n};"; - - return new String[][]{ - // isValidationFailScenario, isLoopsAllowed, script - {"true", "false", scripOne}, - {"true", "false", scripTwo}, - {"true", "false", scripThree}, - {"true", "false", scriptFour}, - {"true", "false", scriptFive}, - {"true", "false", scriptFour}, - {"true", "false", scriptSeven}, - {"false", "false", scripSix}, - {"false", "true", scripOne}, - {"false", "true", scriptFive}, - {"false", "false", scriptEight} - }; - } - - @Test(dataProvider = "validateAdaptiveAuthScriptDataProvider") - public void validateAdaptiveAuthScriptTest(String isValidationFailScenario, String isLoopsAllowed, String script) - throws Exception { - - DefaultApplicationValidator defaultApplicationValidator = new DefaultApplicationValidator(); - - Field configuration = IdentityUtil.class.getDeclaredField("configuration"); - configuration.setAccessible(true); - Map configMap = new HashMap<>(); - configMap.put("AdaptiveAuth.AllowLoops", isLoopsAllowed); - configuration.set(IdentityUtil.class, configMap); - - Method validateAdaptiveAuthScript = DefaultApplicationValidator.class.getDeclaredMethod( - "validateAdaptiveAuthScript", List.class, AuthenticationScriptConfig.class); - validateAdaptiveAuthScript.setAccessible(true); - - AuthenticationScriptConfig scriptConfig = new AuthenticationScriptConfig(); - scriptConfig.setContent(script); - - List validationErrors = new ArrayList<>(); - validateAdaptiveAuthScript.invoke(defaultApplicationValidator, validationErrors, scriptConfig); - - if (Boolean.parseBoolean(isValidationFailScenario)) { - Assert.assertFalse(validationErrors.isEmpty(), "This is an invalid scenario. There should be " + - "validation messages."); - - List filtered = validationErrors.stream() - .filter(error -> StringUtils.containsIgnoreCase(error, "loop")) - .collect(Collectors.toList()); - Assert.assertFalse(filtered.isEmpty(), "There should be a validation message related to loops"); - } else { - Assert.assertTrue(validationErrors.isEmpty(), "There are validation messages. This is a valid case " + - "there should not be any validation messages. Validation messages: " + - String.join("|", validationErrors)); - } - } - - @DataProvider(name = "validateTrustedAppMetadataDataProvider") - public Object[][] validateTrustedAppMetadataDataProvider() { - - String[] validThumbprints = {"thumbprint1", "thumbprint2"}; - String[] inValidThumbprints = Collections.nCopies(21, "thumbprint").toArray(new String[0]); - - return new Object[][]{ - // Valid scenario with both android and iOS configurations. - {"com.wso2.sample", validThumbprints, "sample.app.id", false, false, false}, - // Valid scenario with only android configurations. - {"com.wso2.sample", validThumbprints, "", false, false, false}, - // Valid scenario with only iOS configurations. - {"", new String[0], "sample.app.id", false, false, false}, - // Invalid scenario without both android and iOS configurations. - {"", new String[0], "", false, false, true}, - // Invalid scenario with invalid thumbprints. - {"com.wso2.sample", inValidThumbprints, "sample.app.id", false, false, true}, - // Valid scenario with consent required config enabled. - {"com.wso2.sample", validThumbprints, "sample.app.id", true, true, false}, - // Invalid scenario with consent required config enabled and consent not granted. - {"com.wso2.sample", validThumbprints, "sample.app.id", true, false, true}, - // Invalid scenario with empty android package name and non-empty thumbprints. - {"", validThumbprints, "sample.app.id", true, false, true}, - // Invalid scenario with non-empty android package name and empty thumbprints. - {"com.wso2.sample", new String[0], "sample.app.id", true, false, true} - }; - } - - @Test(dataProvider = "validateTrustedAppMetadataDataProvider") - public void testValidateTrustedAppMetadata(String androidPackageName, String[] thumbprints, String appleAppId, - boolean consentRequired, boolean consentGranted, - boolean isValidationFailScenario) - throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - - ServiceProvider sp = new ServiceProvider(); - SpTrustedAppMetadata spTrustedAppMetadata = new SpTrustedAppMetadata(); - spTrustedAppMetadata.setIsFidoTrusted(true); - spTrustedAppMetadata.setIsConsentGranted(consentGranted); - spTrustedAppMetadata.setAndroidPackageName(androidPackageName); - spTrustedAppMetadata.setAndroidThumbprints(thumbprints); - spTrustedAppMetadata.setAppleAppId(appleAppId); - sp.setTrustedAppMetadata(spTrustedAppMetadata); - - try (MockedStatic identityUtil = Mockito.mockStatic(IdentityUtil.class)) { - identityUtil.when(() -> IdentityUtil.getProperty(TRUSTED_APP_CONSENT_REQUIRED_PROPERTY)). - thenReturn(String.valueOf(consentRequired)); - - List validationErrors = new ArrayList<>(); - - DefaultApplicationValidator defaultApplicationValidator = new DefaultApplicationValidator(); - Method validateTrustedAppMetadata = DefaultApplicationValidator.class.getDeclaredMethod( - "validateTrustedAppMetadata", List.class, ServiceProvider.class); - validateTrustedAppMetadata.setAccessible(true); - validateTrustedAppMetadata.invoke(defaultApplicationValidator, validationErrors, sp); - - if (isValidationFailScenario) { - Assert.assertFalse(validationErrors.isEmpty(), "This is an invalid scenario. There should be " + - "validation messages."); - } else { - Assert.assertTrue(validationErrors.isEmpty(), "There are validation messages. This is a valid case " + - "there should not be any validation messages. Validation messages: " + - String.join("|", validationErrors)); - } - } - } - - @Test - public void testValidateTrustedAppWithEmptyMetadataObj() throws NoSuchMethodException, InvocationTargetException, - IllegalAccessException { - - ServiceProvider sp = new ServiceProvider(); - List validationErrors = new ArrayList<>(); - - DefaultApplicationValidator defaultApplicationValidator = new DefaultApplicationValidator(); - Method validateTrustedAppMetadata = DefaultApplicationValidator.class.getDeclaredMethod( - "validateTrustedAppMetadata", List.class, ServiceProvider.class); - validateTrustedAppMetadata.setAccessible(true); - validateTrustedAppMetadata.invoke(defaultApplicationValidator, validationErrors, sp); - - Assert.assertTrue(validationErrors.isEmpty(), "Validation should be skipped and there should not be any " + - "error messages."); - } - - @DataProvider(name = "validateApplicationVersionDataProvider") - public Object[][] validateApplicationVersionDataProvider() { - - return new Object[][]{ - // version, valid status - {"v2.0.0", "oauth2", true}, - {"v1.0.0", "oauth2", true}, - {"v0.0.0", "oauth2", true}, - {"v0.0.1", "oauth2", false}, - {"v0.1.1", "oauth2", false}, - {"v1.1.1", "oauth2", false}, - {"v2.0.1", "oauth2", false}, - {"dummy", "oauth2", false}, - {"v2.0.0", "samlsso", true}, - {"v1.0.0", "samlsso", true}, - {"v0.0.0", "samlsso", true}, - {"v0.0.1", "samlsso", false}, - {"v0.1.1", "samlsso", false}, - {"v1.1.1", "samlsso", false}, - {"v2.0.1", "samlsso", false}, - {"dummy", "samlsso", false}, - {"v2.0.0", null, true}, - {"v1.0.0", null, true}, - {"v0.0.0", null, true}, - {"v0.0.1", null, false}, - {"v0.1.1", null, false}, - {"v1.1.1", null, false}, - {"v2.0.1", null, false}, - {"dummy", null, false}, - - }; - } - - @Test(dataProvider = "validateApplicationVersionDataProvider") - public void testValidateApplicationVersion(String version, String authType, boolean isValid) - throws NoSuchMethodException, - InvocationTargetException, IllegalAccessException { - - // Prepare the service provider object. - ServiceProvider sp = new ServiceProvider(); - InboundAuthenticationConfig inboundAuthenticationConfig = new InboundAuthenticationConfig(); - if (authType != null) { - InboundAuthenticationRequestConfig inboundAuthenticationRequestConfig - = new InboundAuthenticationRequestConfig(); - inboundAuthenticationRequestConfig.setInboundAuthType(authType); - inboundAuthenticationConfig.setInboundAuthenticationRequestConfigs( - new InboundAuthenticationRequestConfig[]{ inboundAuthenticationRequestConfig}); - sp.setInboundAuthenticationConfig(inboundAuthenticationConfig); - } - sp.setApplicationVersion(version); - List validationErrors = new ArrayList<>(); - - DefaultApplicationValidator defaultApplicationValidator = new DefaultApplicationValidator(); - Method validateApplicationVersion = DefaultApplicationValidator.class.getDeclaredMethod( - "validateApplicationVersion", List.class, ServiceProvider.class); - validateApplicationVersion.setAccessible(true); - validateApplicationVersion.invoke(defaultApplicationValidator, validationErrors, sp); - - Assert.assertEquals(validationErrors.isEmpty(), isValid, "Valid app version has been introduced. " + - "Please update the test case accordingly."); - } -} diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidatorTest.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidatorTest.java new file mode 100644 index 000000000000..c71ec8e9da4a --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidatorTest.java @@ -0,0 +1,648 @@ +/* + * Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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.wso2.carbon.identity.application.mgt.validator; + +import org.apache.commons.lang.StringUtils; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.wso2.carbon.CarbonConstants; +import org.wso2.carbon.base.CarbonBaseConstants; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException; +import org.wso2.carbon.identity.application.common.model.DiscoverableGroup; +import org.wso2.carbon.identity.application.common.model.GroupBasicInfo; +import org.wso2.carbon.identity.application.common.model.InboundAuthenticationConfig; +import org.wso2.carbon.identity.application.common.model.InboundAuthenticationRequestConfig; +import org.wso2.carbon.identity.application.common.model.RequestPathAuthenticatorConfig; +import org.wso2.carbon.identity.application.common.model.ServiceProvider; +import org.wso2.carbon.identity.application.common.model.SpTrustedAppMetadata; +import org.wso2.carbon.identity.application.common.model.script.AuthenticationScriptConfig; +import org.wso2.carbon.identity.application.mgt.ApplicationManagementService; +import org.wso2.carbon.identity.application.mgt.internal.ApplicationManagementServiceComponentHolder; +import org.wso2.carbon.identity.core.util.IdentityTenantUtil; +import org.wso2.carbon.identity.core.util.IdentityUtil; +import org.wso2.carbon.user.core.UserRealm; +import org.wso2.carbon.user.core.UserStoreClientException; +import org.wso2.carbon.user.core.UserStoreException; +import org.wso2.carbon.user.core.common.AbstractUserStoreManager; +import org.wso2.carbon.user.core.service.RealmService; +import org.wso2.carbon.utils.multitenancy.MultitenantConstants; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; +import static org.wso2.carbon.base.MultitenantConstants.SUPER_TENANT_DOMAIN_NAME; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.ErrorMessage.ERROR_CHECKING_GROUP_EXISTENCE; +import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.TRUSTED_APP_CONSENT_REQUIRED_PROPERTY; +import static org.wso2.carbon.utils.multitenancy.MultitenantConstants.SUPER_TENANT_ID; + +/** + * Test class for DefaultApplicationValidator. + */ +public class DefaultApplicationValidatorTest { + + private static final String DEFAULT_USER_STORE_DOMAIN = "PRIMARY"; + private static final String USERNAME = "test-user"; + private static final String USER_ID = "test-user-id"; + + MockedStatic mockIdentityTenantUtil; + MockedStatic mockedApplicationManagementServiceComponentHolder; + ApplicationManagementServiceComponentHolder mockComponentHolder; + UserRealm mockUserRealm; + RealmService mockRealmService; + AbstractUserStoreManager mockAbstractUserStoreManager; + + /** + * Setup the test environment for the test class. + */ + @BeforeClass + public void setup() throws org.wso2.carbon.user.api.UserStoreException { + + mockIdentityTenantUtil = mockStatic(IdentityTenantUtil.class); + mockedApplicationManagementServiceComponentHolder = + mockStatic(ApplicationManagementServiceComponentHolder.class); + mockComponentHolder = mock(ApplicationManagementServiceComponentHolder.class); + mockUserRealm = mock(UserRealm.class); + mockRealmService = mock(RealmService.class); + mockAbstractUserStoreManager = mock(AbstractUserStoreManager.class); + setupInitConfigurations(); + } + + @DataProvider(name = "validateAdaptiveAuthScriptDataProvider") + public Object[][] validateAdaptiveAuthScriptDataProvider() { + + String scripOne = "var onLoginRequest = function(context) {\n" + + " \tfor (var step = 0; step < 5; step++) {\n" + + " \t\tLog.info('Walking east one step');\n" + + "\t} \n" + + " executeStep(1);\n" + + "};\n"; + String scripTwo = "var onLoginRequest = function(context) {for(var step = 0; step < 5; step++) {\n" + + " \t\tLog.info('Walking east one step');\n" + + "\t} \n" + + " executeStep(1);\n" + + "};\n"; + String scripThree = "var onLoginRequest = function(context) {for(var step = 0; step < 5; step++) {" + + "Log.info('Walking east one step');} executeStep(1);};"; + String scriptFour = "var onLoginRequest = function(context) {\n" + + " var n = 0;\n" + + "\tvar x = 0;\n" + + "\tvar abc = 'abc';while (n < 3) {\n" + + "\t n++;\n" + + "\t x += n;\n" + + "\t}\n" + + " executeStep(1);\n" + + "};\n"; + String scriptFive = "var onLoginRequest = function(context) {\n" + + "var i = 0;\n" + + "do {\n" + + " i += 1;\n" + + " console.log(i);\n" + + "}while(i < 5);\n" + + " executeStep(1);\n" + + "};\n"; + String scripSix = "var onLoginRequest = function(context) {\n" + + " var forceAuth = true;\n" + + " var therefore = true;\n" + + " var fivefor = true;\n" + + " var for_auth = true;\n" + + " var _for = true;\n" + + " var whilefor = true;\n" + + " var forwhile = true;\n" + + " var some_whiles = true;\n" + + " var fore = true;\n" + + " var foreach = true;\n" + + " var foreaches = true;\n" + + " executeStep(1);\n" + + "};\n"; + + String scriptSeven = "var onLoginRequest = function(context) {\n" + + " var array1 = ['a', 'b', 'c'];\n" + + " array1.forEach(element => console.log(element));\n" + + " executeStep(1);\n" + + "};\n"; + + String scriptEight = "// This script will only allow for login to application while the user's age is over " + + "configured value\n// The user will be redirected to an error page if the date of birth is not " + + "present or user is below configured var onLoginRequest = function(context) {for(var step = 0;" + + " step < 5; step++) {Log.info('Walking east one step');} executeStep(1);} value\n\nvar ageLimit" + + " = 18;\n\n// Error page to redirect " + + "unauthorized users,\n// can be either an absolute url or relative url to server root, or " + + "empty/null\n// null/empty value will redirect to the default error page\nvar errorPage = '';\n\n// " + + "Additional query params to be added to the above url.\n// Hint: Use i18n keys for error " + + "messages\nvar errorPageParameters = {\n 'status': 'Unauthorized',\n 'statusMsg': \"You need to " + + "be over \" + ageLimit + \" for login to this application.\"\n};\n\n/*\nHi am a multi line comment " + + "1\n*/\n// Date of birth attribute at the client side\nvar dateOfBirthClaim = 'http://wso2" + + ".org/claims/dob';\n\n// The validator function for DOB. Default validation check if the DOB is in " + + "YYYY-MM-dd format\nvar validateDOB = function (dob) {\n return dob.match(/^(\\d{4})-(\\d{2})-" + + "(\\d{2})$/);\n};\n\nvar onLoginRequest = function(context) {\n executeStep(1, {\n " + + "onSuccess: function (context) {\n var underAge = true;\n // Extracting user " + + "store domain of authenticated subject from the first step\n var dob = context" + + ".currentKnownSubject.localClaims[dateOfBirthClaim];// Adding a single line comment with for while " + + "foreach \n Log.debug('DOB for user ' + context.currentKnownSubject.identifier + ' is : " + + "' + dob);\n Log.debug('while for forEach ');\n if (dob && validateDOB(dob)) {\n var birthDate = new " + + "Date(dob);\n if (getAge(birthDate) >= ageLimit) {\n underAge = false;\n " + + " }\n }\n if (underAge === true) {\n Log.debug('User ' +" + + " context.currentKnownSubject.identifier + ' is under aged. Hence denied to login.');\n " + + " sendError(errorPage, errorPageParameters);\n }\n }\n });\n};\n/*\n* Hi am a " + + "multi line comment 2\n*/\nvar getAge = function(birthDate) {\n var /*Adding a multiline comment " + + "with for while foreach*/today = new Date();\n var age = today.getFullYear() - birthDate" + + ".getFullYear();\n var m = today.getMonth()/* Adding a multiline comment with for while foreach */" + + " - birthDate.getMonth();\n if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {\n " + + " age--;\n }\n return age;\n};"; + + return new String[][]{ + // isValidationFailScenario, isLoopsAllowed, script + {"true", "false", scripOne}, + {"true", "false", scripTwo}, + {"true", "false", scripThree}, + {"true", "false", scriptFour}, + {"true", "false", scriptFive}, + {"true", "false", scriptFour}, + {"true", "false", scriptSeven}, + {"false", "false", scripSix}, + {"false", "true", scripOne}, + {"false", "true", scriptFive}, + {"false", "false", scriptEight} + }; + } + + @Test(dataProvider = "validateAdaptiveAuthScriptDataProvider") + public void validateAdaptiveAuthScriptTest(String isValidationFailScenario, String isLoopsAllowed, String script) + throws Exception { + + DefaultApplicationValidator defaultApplicationValidator = new DefaultApplicationValidator(); + + Field configuration = IdentityUtil.class.getDeclaredField("configuration"); + configuration.setAccessible(true); + Map configMap = new HashMap<>(); + configMap.put("AdaptiveAuth.AllowLoops", isLoopsAllowed); + configuration.set(IdentityUtil.class, configMap); + + Method validateAdaptiveAuthScript = DefaultApplicationValidator.class.getDeclaredMethod( + "validateAdaptiveAuthScript", List.class, AuthenticationScriptConfig.class); + validateAdaptiveAuthScript.setAccessible(true); + + AuthenticationScriptConfig scriptConfig = new AuthenticationScriptConfig(); + scriptConfig.setContent(script); + + List validationErrors = new ArrayList<>(); + validateAdaptiveAuthScript.invoke(defaultApplicationValidator, validationErrors, scriptConfig); + + if (Boolean.parseBoolean(isValidationFailScenario)) { + Assert.assertFalse(validationErrors.isEmpty(), "This is an invalid scenario. There should be " + + "validation messages."); + + List filtered = validationErrors.stream() + .filter(error -> StringUtils.containsIgnoreCase(error, "loop")) + .collect(Collectors.toList()); + Assert.assertFalse(filtered.isEmpty(), "There should be a validation message related to loops"); + } else { + Assert.assertTrue(validationErrors.isEmpty(), "There are validation messages. This is a valid case " + + "there should not be any validation messages. Validation messages: " + + String.join("|", validationErrors)); + } + } + + @DataProvider(name = "validateTrustedAppMetadataDataProvider") + public Object[][] validateTrustedAppMetadataDataProvider() { + + String[] validThumbprints = {"thumbprint1", "thumbprint2"}; + String[] inValidThumbprints = Collections.nCopies(21, "thumbprint").toArray(new String[0]); + + return new Object[][]{ + // Valid scenario with both android and iOS configurations. + {"com.wso2.sample", validThumbprints, "sample.app.id", false, false, false}, + // Valid scenario with only android configurations. + {"com.wso2.sample", validThumbprints, "", false, false, false}, + // Valid scenario with only iOS configurations. + {"", new String[0], "sample.app.id", false, false, false}, + // Invalid scenario without both android and iOS configurations. + {"", new String[0], "", false, false, true}, + // Invalid scenario with invalid thumbprints. + {"com.wso2.sample", inValidThumbprints, "sample.app.id", false, false, true}, + // Valid scenario with consent required config enabled. + {"com.wso2.sample", validThumbprints, "sample.app.id", true, true, false}, + // Invalid scenario with consent required config enabled and consent not granted. + {"com.wso2.sample", validThumbprints, "sample.app.id", true, false, true}, + // Invalid scenario with empty android package name and non-empty thumbprints. + {"", validThumbprints, "sample.app.id", true, false, true}, + // Invalid scenario with non-empty android package name and empty thumbprints. + {"com.wso2.sample", new String[0], "sample.app.id", true, false, true} + }; + } + + @Test(dataProvider = "validateTrustedAppMetadataDataProvider") + public void testValidateTrustedAppMetadata(String androidPackageName, String[] thumbprints, String appleAppId, + boolean consentRequired, boolean consentGranted, + boolean isValidationFailScenario) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + + ServiceProvider sp = new ServiceProvider(); + SpTrustedAppMetadata spTrustedAppMetadata = new SpTrustedAppMetadata(); + spTrustedAppMetadata.setIsFidoTrusted(true); + spTrustedAppMetadata.setIsConsentGranted(consentGranted); + spTrustedAppMetadata.setAndroidPackageName(androidPackageName); + spTrustedAppMetadata.setAndroidThumbprints(thumbprints); + spTrustedAppMetadata.setAppleAppId(appleAppId); + sp.setTrustedAppMetadata(spTrustedAppMetadata); + + try (MockedStatic identityUtil = Mockito.mockStatic(IdentityUtil.class)) { + identityUtil.when(() -> IdentityUtil.getProperty(TRUSTED_APP_CONSENT_REQUIRED_PROPERTY)). + thenReturn(String.valueOf(consentRequired)); + + List validationErrors = new ArrayList<>(); + + DefaultApplicationValidator defaultApplicationValidator = new DefaultApplicationValidator(); + Method validateTrustedAppMetadata = DefaultApplicationValidator.class.getDeclaredMethod( + "validateTrustedAppMetadata", List.class, ServiceProvider.class); + validateTrustedAppMetadata.setAccessible(true); + validateTrustedAppMetadata.invoke(defaultApplicationValidator, validationErrors, sp); + + if (isValidationFailScenario) { + Assert.assertFalse(validationErrors.isEmpty(), "This is an invalid scenario. There should be " + + "validation messages."); + } else { + Assert.assertTrue(validationErrors.isEmpty(), "There are validation messages. This is a valid case " + + "there should not be any validation messages. Validation messages: " + + String.join("|", validationErrors)); + } + } + } + + @Test + public void testValidateTrustedAppWithEmptyMetadataObj() throws NoSuchMethodException, InvocationTargetException, + IllegalAccessException { + + ServiceProvider sp = new ServiceProvider(); + List validationErrors = new ArrayList<>(); + + DefaultApplicationValidator defaultApplicationValidator = new DefaultApplicationValidator(); + Method validateTrustedAppMetadata = DefaultApplicationValidator.class.getDeclaredMethod( + "validateTrustedAppMetadata", List.class, ServiceProvider.class); + validateTrustedAppMetadata.setAccessible(true); + validateTrustedAppMetadata.invoke(defaultApplicationValidator, validationErrors, sp); + + Assert.assertTrue(validationErrors.isEmpty(), "Validation should be skipped and there should not be any " + + "error messages."); + } + + @DataProvider(name = "validateApplicationVersionDataProvider") + public Object[][] validateApplicationVersionDataProvider() { + + return new Object[][]{ + // version, valid status + {"v2.0.0", "oauth2", true}, + {"v1.0.0", "oauth2", true}, + {"v0.0.0", "oauth2", true}, + {"v0.0.1", "oauth2", false}, + {"v0.1.1", "oauth2", false}, + {"v1.1.1", "oauth2", false}, + {"v2.0.1", "oauth2", false}, + {"dummy", "oauth2", false}, + {"v2.0.0", "samlsso", true}, + {"v1.0.0", "samlsso", true}, + {"v0.0.0", "samlsso", true}, + {"v0.0.1", "samlsso", false}, + {"v0.1.1", "samlsso", false}, + {"v1.1.1", "samlsso", false}, + {"v2.0.1", "samlsso", false}, + {"dummy", "samlsso", false}, + {"v2.0.0", null, true}, + {"v1.0.0", null, true}, + {"v0.0.0", null, true}, + {"v0.0.1", null, false}, + {"v0.1.1", null, false}, + {"v1.1.1", null, false}, + {"v2.0.1", null, false}, + {"dummy", null, false}, + + }; + } + + @Test(dataProvider = "validateApplicationVersionDataProvider") + public void testValidateApplicationVersion(String version, String authType, boolean isValid) + throws NoSuchMethodException, + InvocationTargetException, IllegalAccessException { + + // Prepare the service provider object. + ServiceProvider sp = new ServiceProvider(); + InboundAuthenticationConfig inboundAuthenticationConfig = new InboundAuthenticationConfig(); + if (authType != null) { + InboundAuthenticationRequestConfig inboundAuthenticationRequestConfig + = new InboundAuthenticationRequestConfig(); + inboundAuthenticationRequestConfig.setInboundAuthType(authType); + inboundAuthenticationConfig.setInboundAuthenticationRequestConfigs( + new InboundAuthenticationRequestConfig[]{ inboundAuthenticationRequestConfig}); + sp.setInboundAuthenticationConfig(inboundAuthenticationConfig); + } + sp.setApplicationVersion(version); + List validationErrors = new ArrayList<>(); + + DefaultApplicationValidator defaultApplicationValidator = new DefaultApplicationValidator(); + Method validateApplicationVersion = DefaultApplicationValidator.class.getDeclaredMethod( + "validateApplicationVersion", List.class, ServiceProvider.class); + validateApplicationVersion.setAccessible(true); + validateApplicationVersion.invoke(defaultApplicationValidator, validationErrors, sp); + + assertEquals(validationErrors.isEmpty(), isValid, "Valid app version has been introduced. " + + "Please update the test case accordingly."); + } + + @Test(description = "Validate error message for discoverable groups in a non-discoverable application") + public void testValidateDiscoverableGroupsForNonDiscoverableApp() throws IdentityApplicationManagementException { + + validateDiscoverableGroupsCommon((sp) -> { + sp.setDiscoverable(false); + sp.setDiscoverableGroups( + new DiscoverableGroup[] {getNewDiscoverableGroup(DEFAULT_USER_STORE_DOMAIN, 1, 0)}); + ApplicationValidator applicationValidator = new DefaultApplicationValidator(); + try { + List validationErrors = + applicationValidator.validateApplication(sp, SUPER_TENANT_DOMAIN_NAME, USERNAME); + assertEquals(validationErrors.size(), 1); + assertEquals(validationErrors.get(0), + "Discoverable groups are defined for a non-discoverable application."); + } catch (IdentityApplicationManagementException e) { + fail("Unexpected Exception occurred.", e); + } + }); + } + + @Test(description = "Validate null user store in discoverable groups") + public void testValidateNullUserStoreInDiscoverableGroups() throws IdentityApplicationManagementException { + + validateDiscoverableGroupsCommon((sp) -> { + sp.setDiscoverableGroups(new DiscoverableGroup[] {getNewDiscoverableGroup(null, 1, 0), + getNewDiscoverableGroup(null, 1, 0)}); + ApplicationValidator applicationValidator = new DefaultApplicationValidator(); + try { + List validationErrors = + applicationValidator.validateApplication(sp, SUPER_TENANT_DOMAIN_NAME, USERNAME); + assertEquals(validationErrors.size(), 2); + assertEquals(validationErrors.get(0), + "No user store defined for the discoverable groups indexed at 0."); + assertEquals(validationErrors.get(1), + "No user store defined for the discoverable groups indexed at 1."); + } catch (IdentityApplicationManagementException e) { + fail("Unexpected Exception occurred.", e); + } + }); + } + + @Test(description = "Validate non-existing user store in discoverable groups") + public void testValidateNonExistingUserStoreInDiscoverableGroups() throws IdentityApplicationManagementException { + + validateDiscoverableGroupsCommon((sp) -> { + when(mockAbstractUserStoreManager.getSecondaryUserStoreManager("non-existing")).thenReturn(null); + sp.setDiscoverableGroups(new DiscoverableGroup[] {getNewDiscoverableGroup("non-existing", 1, 0), + getNewDiscoverableGroup("non-existing", 1, 0)}); + ApplicationValidator applicationValidator = new DefaultApplicationValidator(); + try { + List validationErrors = + applicationValidator.validateApplication(sp, SUPER_TENANT_DOMAIN_NAME, USERNAME); + assertEquals(validationErrors.size(), 2); + assertEquals(validationErrors.get(0), + "The provided user store is not found for the discoverable groups indexed at 0."); + assertEquals(validationErrors.get(1), + "The provided user store is not found for the discoverable groups indexed at 1."); + } catch (IdentityApplicationManagementException e) { + fail("Unexpected Exception occurred.", e); + } + }); + } + + @Test(description = "Validate empty or null groups list in discoverable groups") + public void testValidateEmptyOrNullGroupsList() throws IdentityApplicationManagementException { + + validateDiscoverableGroupsCommon((sp) -> { + when(mockAbstractUserStoreManager.getSecondaryUserStoreManager(DEFAULT_USER_STORE_DOMAIN)).thenReturn( + mockAbstractUserStoreManager); + DiscoverableGroup discoverableGroup = getNewDiscoverableGroup(DEFAULT_USER_STORE_DOMAIN, 0, 0); + sp.setDiscoverableGroups(new DiscoverableGroup[] { discoverableGroup }); + ApplicationValidator applicationValidator = new DefaultApplicationValidator(); + try { + List validationErrors = + applicationValidator.validateApplication(sp, SUPER_TENANT_DOMAIN_NAME, USERNAME); + assertEquals(validationErrors.size(), 1); + assertEquals(validationErrors.get(0), + "No groups defined for the user store: 'PRIMARY' in the discoverable groups configuration."); + discoverableGroup.setGroups(null); + validationErrors = applicationValidator.validateApplication(sp, SUPER_TENANT_DOMAIN_NAME, USERNAME); + assertEquals(validationErrors.size(), 1); + assertEquals(validationErrors.get(0), + "No groups defined for the user store: 'PRIMARY' in the discoverable groups configuration."); + } catch (IdentityApplicationManagementException e) { + fail("Unexpected Exception occurred.", e); + } + }); + } + + @Test(description = "Validate null group ids within discoverable groups") + public void testValidateNullGroupIdsInDiscoverableGroups() throws IdentityApplicationManagementException { + + validateDiscoverableGroupsCommon((sp) -> { + when(mockAbstractUserStoreManager.getSecondaryUserStoreManager(DEFAULT_USER_STORE_DOMAIN)).thenReturn( + mockAbstractUserStoreManager); + DiscoverableGroup discoverableGroup = getNewDiscoverableGroup(DEFAULT_USER_STORE_DOMAIN, 2, 0); + discoverableGroup.getGroups()[0].setId(null); + discoverableGroup.getGroups()[1].setId(null); + sp.setDiscoverableGroups(new DiscoverableGroup[] {discoverableGroup}); + ApplicationValidator applicationValidator = new DefaultApplicationValidator(); + try { + List validationErrors = + applicationValidator.validateApplication(sp, SUPER_TENANT_DOMAIN_NAME, USERNAME); + assertEquals(validationErrors.size(), 2); + assertEquals(validationErrors.get(0), + "Group ID is not defined for the group indexed at 0 for the user store: 'PRIMARY' in the" + + " discoverable groups configuration."); + assertEquals(validationErrors.get(1), + "Group ID is not defined for the group indexed at 1 for the user store: 'PRIMARY' in the" + + " discoverable groups configuration."); + } catch (IdentityApplicationManagementException e) { + fail("Unexpected Exception occurred.", e); + } + }); + } + + @Test(description = "Validate the error message when the group id is incorrect") + public void testValidateIncorrectGroupId() throws IdentityApplicationManagementException { + + validateDiscoverableGroupsCommon((sp) -> { + when(mockAbstractUserStoreManager.getSecondaryUserStoreManager(DEFAULT_USER_STORE_DOMAIN)).thenReturn( + mockAbstractUserStoreManager); + DiscoverableGroup discoverableGroup = getNewDiscoverableGroup(DEFAULT_USER_STORE_DOMAIN, 2, 0); + sp.setDiscoverableGroups(new DiscoverableGroup[] {discoverableGroup}); + ApplicationValidator applicationValidator = new DefaultApplicationValidator(); + try { + when(mockAbstractUserStoreManager.getGroupNameByGroupId(eq("test-group-id-0"))).thenThrow( + new UserStoreClientException()); + when(mockAbstractUserStoreManager.getGroupNameByGroupId(eq("test-group-id-1"))).thenThrow( + new UserStoreClientException()); + List validationErrors = + applicationValidator.validateApplication(sp, SUPER_TENANT_DOMAIN_NAME, USERNAME); + assertEquals(validationErrors.size(), 2); + assertEquals(validationErrors.get(0), "No group found for the given group ID: 'test-group-id-0'."); + assertEquals(validationErrors.get(1), "No group found for the given group ID: 'test-group-id-1'."); + } catch (IdentityApplicationManagementException | UserStoreException e) { + fail("Unexpected Exception occurred.", e); + } + }); + } + + @Test(description = "Validate the failure of group existence checking") + public void testValidateGroupExistenceCheckFailure() throws IdentityApplicationManagementException { + + validateDiscoverableGroupsCommon((sp) -> { + when(mockAbstractUserStoreManager.getSecondaryUserStoreManager(DEFAULT_USER_STORE_DOMAIN)).thenReturn( + mockAbstractUserStoreManager); + DiscoverableGroup discoverableGroup = getNewDiscoverableGroup(DEFAULT_USER_STORE_DOMAIN, 2, 0); + sp.setDiscoverableGroups(new DiscoverableGroup[] {discoverableGroup}); + ApplicationValidator applicationValidator = new DefaultApplicationValidator(); + try { + when(mockAbstractUserStoreManager.getGroupNameByGroupId(eq("test-group-id-0"))).thenThrow( + new UserStoreException()); + applicationValidator.validateApplication(sp, SUPER_TENANT_DOMAIN_NAME, USERNAME); + } catch (IdentityApplicationManagementException | UserStoreException e) { + assert e instanceof IdentityApplicationManagementException; + assertEquals(((IdentityApplicationManagementException) e).getErrorCode(), + ERROR_CHECKING_GROUP_EXISTENCE.getCode()); + } + }); + } + + @Test(description = "Validate the success scenario of discoverable groups validation") + private void testValidateDiscoverableGroupsSuccess() throws IdentityApplicationManagementException { + + validateDiscoverableGroupsCommon((sp) -> { + when(mockAbstractUserStoreManager.getSecondaryUserStoreManager(DEFAULT_USER_STORE_DOMAIN)).thenReturn( + mockAbstractUserStoreManager); + DiscoverableGroup discoverableGroup = getNewDiscoverableGroup(DEFAULT_USER_STORE_DOMAIN, 1, 0); + sp.setDiscoverableGroups(new DiscoverableGroup[] {discoverableGroup}); + ApplicationValidator applicationValidator = new DefaultApplicationValidator(); + try { + when(mockAbstractUserStoreManager.getGroupNameByGroupId(eq("test-group-id-0"))).thenReturn( + "test-group-name-0"); + List validationErrors = + applicationValidator.validateApplication(sp, SUPER_TENANT_DOMAIN_NAME, USERNAME); + assertEquals(validationErrors.size(), 0); + } catch (IdentityApplicationManagementException | UserStoreException e) { + fail("Unexpected Exception occurred.", e); + } + }); + } + + /** + * Common method to validate discoverable groups by giving a custom assertion function. + * + * @param assertionFunc Assertion function. + * @throws IdentityApplicationManagementException If an error occurs while validating discoverable groups. + */ + private void validateDiscoverableGroupsCommon(Consumer assertionFunc) + throws IdentityApplicationManagementException { + + ApplicationManagementService applicationManagementService = mock(ApplicationManagementService.class); + when(applicationManagementService.getAllRequestPathAuthenticators(eq(SUPER_TENANT_DOMAIN_NAME))).thenReturn( + new RequestPathAuthenticatorConfig[0]); + try (MockedStatic staticApplicationManagementService = Mockito.mockStatic( + ApplicationManagementService.class)) { + staticApplicationManagementService.when(ApplicationManagementService::getInstance).thenReturn( + applicationManagementService); + ServiceProvider sp = new ServiceProvider(); + sp.setApplicationName("test-app"); + sp.setApplicationVersion("v1.0.0"); + sp.setDiscoverable(true); + sp.setAccessUrl("https://localhost:5000/test-app"); + assertionFunc.accept(sp); + } + } + + /** + * Get a new DiscoverableGroup object. + * + * @param userStore User store domain. + * @param numberOfGroups Number of groups to be added. + * @param startIndex Suffix start index of the group. + * @return New DiscoverableGroup object. + */ + private DiscoverableGroup getNewDiscoverableGroup(String userStore, int numberOfGroups, int startIndex) { + + DiscoverableGroup discoverableGroup = new DiscoverableGroup(); + discoverableGroup.setUserStore(userStore); + List groupBasicInfos = new ArrayList<>(); + for (int i = startIndex; i < numberOfGroups + startIndex; i++) { + GroupBasicInfo groupBasicInfo = new GroupBasicInfo(); + groupBasicInfo.setId("test-group-id-" + i); + groupBasicInfo.setName("test-group-name-" + i); + groupBasicInfos.add(groupBasicInfo); + } + discoverableGroup.setGroups(groupBasicInfos.toArray(new GroupBasicInfo[0])); + return discoverableGroup; + } + + /** + * Setup the configurations for the test. + */ + private void setupInitConfigurations() throws org.wso2.carbon.user.api.UserStoreException { + + String carbonHome = Paths.get(System.getProperty("user.dir"), "target", "test-classes", "repository"). + toString(); + System.setProperty(CarbonBaseConstants.CARBON_HOME, carbonHome); + System.setProperty(CarbonBaseConstants.CARBON_CONFIG_DIR_PATH, Paths.get(carbonHome, "conf").toString()); + + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain( + MultitenantConstants.SUPER_TENANT_DOMAIN_NAME); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(SUPER_TENANT_ID); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(USERNAME); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setUserId(USER_ID); + + CarbonConstants.ENABLE_LEGACY_AUTHZ_RUNTIME = false; + + mockIdentityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(eq( + MultitenantConstants.SUPER_TENANT_DOMAIN_NAME))) + .thenReturn(SUPER_TENANT_ID); + + mockedApplicationManagementServiceComponentHolder.when( + ApplicationManagementServiceComponentHolder::getInstance) + .thenReturn(mockComponentHolder); + when(mockComponentHolder.getRealmService()).thenReturn(mockRealmService); + when(mockRealmService.getTenantUserRealm(SUPER_TENANT_ID)).thenReturn(mockUserRealm); + when(mockUserRealm.getUserStoreManager()).thenReturn(mockAbstractUserStoreManager); + } +} diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/resources/testng.xml b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/resources/testng.xml index a73717f257d0..d837e10f2889 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/resources/testng.xml +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/resources/testng.xml @@ -21,7 +21,7 @@ - + From 7580ecbf8f942fddf95b2d9c02eca398d2c13b22 Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Wed, 29 Jan 2025 23:56:56 +0530 Subject: [PATCH 18/23] close the mocked static classes --- .../identity/application/mgt/ApplicationMgtUtilTest.java | 1 - .../mgt/validator/DefaultApplicationValidatorTest.java | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtilTest.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtilTest.java index 1f2ea1315f07..1675a4644787 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtilTest.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/ApplicationMgtUtilTest.java @@ -20,7 +20,6 @@ import org.mockito.stubbing.Answer; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; -import org.testng.annotations.Ignore; import org.testng.annotations.Test; import org.wso2.carbon.base.CarbonBaseConstants; import org.wso2.carbon.base.ServerConfiguration; diff --git a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidatorTest.java b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidatorTest.java index c71ec8e9da4a..07ebf07f3580 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidatorTest.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.mgt/src/test/java/org/wso2/carbon/identity/application/mgt/validator/DefaultApplicationValidatorTest.java @@ -22,6 +22,7 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; import org.testng.Assert; +import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -103,6 +104,13 @@ public void setup() throws org.wso2.carbon.user.api.UserStoreException { setupInitConfigurations(); } + @AfterClass + public void end() { + + mockIdentityTenantUtil.close(); + mockedApplicationManagementServiceComponentHolder.close(); + } + @DataProvider(name = "validateAdaptiveAuthScriptDataProvider") public Object[][] validateAdaptiveAuthScriptDataProvider() { From 8dabebf4b18b00122a17b37b46ad55db9569c8ec Mon Sep 17 00:00:00 2001 From: DilshanSenarath <74205483+DilshanSenarath@users.noreply.github.com> Date: Thu, 30 Jan 2025 10:20:47 +0530 Subject: [PATCH 19/23] update copyright headers --- .../identity/application/common/model/ServiceProvider.java | 2 +- .../src/test/resources/testng.xml | 2 +- .../org.wso2.carbon.identity.application.mgt/pom.xml | 6 ------ .../identity/application/mgt/ApplicationConstants.java | 2 +- .../application/mgt/ApplicationManagementService.java | 2 +- .../application/mgt/ApplicationManagementServiceImpl.java | 2 +- .../carbon/identity/application/mgt/ApplicationMgtUtil.java | 2 +- .../application/mgt/dao/impl/ApplicationDAOImpl.java | 2 +- .../application/mgt/dao/impl/ApplicationMgtDBQueries.java | 2 +- .../mgt/validator/DefaultApplicationValidator.java | 2 +- .../mgt/ApplicationManagementServiceImplTest.java | 2 +- .../identity/application/mgt/ApplicationMgtUtilTest.java | 2 +- .../mgt/validator/DefaultApplicationValidatorTest.java | 2 +- .../test/resources/repository/conf/identity/identity.xml | 2 +- .../src/test/resources/testng.xml | 2 +- 15 files changed, 14 insertions(+), 20 deletions(-) diff --git a/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/ServiceProvider.java b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/ServiceProvider.java index 604e652c86e3..ea64cabbc261 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/ServiceProvider.java +++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/main/java/org/wso2/carbon/identity/application/common/model/ServiceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2023, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2014-2025, WSO2 LLC. (http://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/resources/testng.xml b/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/resources/testng.xml index 2f06e45b3757..1ef1aa68b5dc 100644 --- a/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/resources/testng.xml +++ b/components/application-mgt/org.wso2.carbon.identity.application.common/src/test/resources/testng.xml @@ -1,5 +1,5 @@