Skip to content

Commit

Permalink
Add new authenticator property.
Browse files Browse the repository at this point in the history
  • Loading branch information
Thisara-Welmilla committed Sep 13, 2024
1 parent 8ddc6a3 commit 36a46fc
Show file tree
Hide file tree
Showing 25 changed files with 122 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,7 @@ CREATE TABLE IF NOT EXISTS IDP_AUTHENTICATOR (
NAME VARCHAR(255) NOT NULL,
IS_ENABLED CHAR (1) DEFAULT '1',
DISPLAY_NAME VARCHAR(255),
DEFINED_BY VARCHAR(255) NOT NULL,
PRIMARY KEY (ID),
UNIQUE (TENANT_ID, IDP_ID, NAME),
FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@ CREATE TABLE IF NOT EXISTS IDP_AUTHENTICATOR (
NAME VARCHAR(255) NOT NULL,
IS_ENABLED CHAR (1) DEFAULT '1',
DISPLAY_NAME VARCHAR(255),
DEFINED_BY VARCHAR(255) NOT NULL,
PRIMARY KEY (ID),
UNIQUE (TENANT_ID, IDP_ID, NAME),
FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.apache.axiom.om.OMElement;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.wso2.carbon.identity.base.IdentityConstants;

import java.io.Serializable;
import java.util.ArrayList;
Expand Down Expand Up @@ -63,6 +64,8 @@ public class FederatedAuthenticatorConfig implements Serializable {
@XmlElement(name = "Tags")
protected String[] tags;

protected IdentityConstants.DefinedByType definedByType;

public static FederatedAuthenticatorConfig build(OMElement federatedAuthenticatorConfigOM) {

if (federatedAuthenticatorConfigOM == null) {
Expand Down Expand Up @@ -230,4 +233,24 @@ public void setTags(String[] tagList) {

tags = tagList;
}

/**
* Get the tag list of the Local authenticator.
*
* @return String[]
*/
public IdentityConstants.DefinedByType getDefinedByType() {

return definedByType;
}

/**
* Set the tag list for Local authenticator config.
*
* @param type authenticator.
*/
public void setDefinedByType(IdentityConstants.DefinedByType type) {

definedByType = type;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ public class LocalAuthenticatorConfig implements Serializable {
@XmlElement(name = "Tags")
protected String[] tags;

protected IdentityConstants.DefinedByType definedByType;

/*
* <LocalAuthenticatorConfig> <Name></Name> <DisplayName></DisplayName> <IsEnabled></IsEnabled>
* <Properties></Properties> </LocalAuthenticatorConfig>
Expand Down Expand Up @@ -224,4 +226,24 @@ public void setTags(String[] tagList) {

tags = tagList;
}

/**
* Get the tag list of the Local authenticator.
*
* @return String[]
*/
public IdentityConstants.DefinedByType getDefinedByType() {

return definedByType;
}

/**
* Set the tag list for Local authenticator config.
*
* @param type authenticator.
*/
public void setDefinedByType(IdentityConstants.DefinedByType type) {

definedByType = type;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ public class ApplicationMgtDBQueries {
"B.DISPLAY_NAME FROM IDP A JOIN IDP_AUTHENTICATOR B ON A.ID = B.IDP_ID WHERE B.ID =? AND ((A.TENANT_ID =?" +
" AND B.TENANT_ID =?) OR (A.TENANT_ID=? AND A.NAME LIKE 'SHARED_%' AND B.TENANT_ID=?))";
public static final String STORE_LOCAL_AUTHENTICATOR = "INSERT INTO IDP_AUTHENTICATOR (TENANT_ID, IDP_ID, NAME," +
"IS_ENABLED, DISPLAY_NAME) VALUES (?, (SELECT ID FROM IDP WHERE IDP.NAME=? AND IDP.TENANT_ID =?), ?, ?, ?)";
"IS_ENABLED, DISPLAY_NAME, SCOPE) VALUES (?, (SELECT ID FROM IDP WHERE IDP.NAME=? AND IDP.TENANT_ID =?), ?, ?, ?, ?)";

public static final String GET_SP_METADATA_BY_SP_ID = "SELECT ID, NAME, VALUE, DISPLAY_NAME FROM SP_METADATA " +
"WHERE SP_ID = ?";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
import org.wso2.carbon.identity.application.mgt.dao.PaginatableFilterableApplicationDAO;
import org.wso2.carbon.identity.application.mgt.internal.ApplicationManagementServiceComponent;
import org.wso2.carbon.identity.application.mgt.internal.ApplicationManagementServiceComponentHolder;
import org.wso2.carbon.identity.base.IdentityConstants;
import org.wso2.carbon.identity.base.IdentityException;
import org.wso2.carbon.identity.base.IdentityRuntimeException;
import org.wso2.carbon.identity.core.CertificateRetrievingException;
Expand Down Expand Up @@ -1566,6 +1567,9 @@ private void updateLocalAndOutboundAuthenticationConfiguration(int applicationId
ApplicationConstants.LOCAL_IDP_NAME,
lclAuthenticator.getName(),
lclAuthenticator.getDisplayName());
} else {
addAuthenticatorDefinedByType(connection, tenantID, authenticatorId,
lclAuthenticator.getDefinedByType().toString());
}
if (authenticatorId > 0) {
// ID, TENANT_ID, AUTHENTICATOR_ID
Expand Down Expand Up @@ -5038,7 +5042,7 @@ private int addAuthenticator(Connection conn, int tenantId, String idpName,
int authenticatorId = -1;
PreparedStatement prepStmt = null;
ResultSet rs = null;
// TENANT_ID, IDP_ID, NAME,IS_ENABLED, DISPLAY_NAME
// TENANT_ID, IDP_ID, NAME,IS_ENABLED, DISPLAY_NAME, DEFINED_BY
String sqlStmt = ApplicationMgtDBQueries.STORE_LOCAL_AUTHENTICATOR;
try {
String dbProductName = conn.getMetaData().getDatabaseProductName();
Expand All @@ -5050,6 +5054,7 @@ private int addAuthenticator(Connection conn, int tenantId, String idpName,
prepStmt.setString(4, authenticatorName);
prepStmt.setString(5, "1");
prepStmt.setString(6, authenticatorDispalyName);
prepStmt.setString(7, IdentityConstants.DefinedByType.SYSTEM.toString());
prepStmt.execute();
rs = prepStmt.getGeneratedKeys();
if (rs.next()) {
Expand All @@ -5061,6 +5066,25 @@ private int addAuthenticator(Connection conn, int tenantId, String idpName,
return authenticatorId;
}

private void addAuthenticatorDefinedByType(Connection conn, int tenantId, int authenticatorId,
String authenticatorDefinedByType) throws SQLException {

PreparedStatement prepStmt = null;
ResultSet rs = null;
String sqlStmt = ApplicationMgtDBQueries.UPDATE_AUTHENTICATOR_DEFINED_BY_TYPE;
try {
String dbProductName = conn.getMetaData().getDatabaseProductName();
prepStmt = conn.prepareStatement(sqlStmt, new String[] {
DBUtils.getConvertedAutoGeneratedColumnName(dbProductName, "ID")});
prepStmt.setString(1, authenticatorDefinedByType);
prepStmt.setInt(2, authenticatorId);
prepStmt.setInt(3, tenantId);
prepStmt.execute();
} finally {
IdentityApplicationManagementUtil.closeStatement(prepStmt);
}
}

/**
* Read application role permissions for a given application name
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,9 @@ public class ApplicationMgtDBQueries {
"B.DISPLAY_NAME FROM IDP A JOIN IDP_AUTHENTICATOR B ON A.ID = B.IDP_ID WHERE B.ID =? AND ((A.TENANT_ID =?" +
" AND B.TENANT_ID =?) OR (A.TENANT_ID=? AND A.NAME LIKE 'SHARED_%' AND B.TENANT_ID=?))";
public static final String STORE_LOCAL_AUTHENTICATOR = "INSERT INTO IDP_AUTHENTICATOR (TENANT_ID, IDP_ID, NAME," +
"IS_ENABLED, DISPLAY_NAME) VALUES (?, (SELECT ID FROM IDP WHERE IDP.NAME=? AND IDP.TENANT_ID =?), ?, ?, ?)";
"IS_ENABLED, DISPLAY_NAME, DEFINED_BY) VALUES (?, (SELECT ID FROM IDP WHERE IDP.NAME=? AND IDP.TENANT_ID =?), ?, ?, ?, ?)";
public static final String UPDATE_AUTHENTICATOR_DEFINED_BY_TYPE = "UPDATE IDP_AUTHENTICATOR SET " +
"DEFINED_BY= ? WHERE ID = ? AND TENANT_ID = ?";

public static final String GET_SP_METADATA_BY_SP_ID = "SELECT ID, NAME, VALUE, DISPLAY_NAME FROM SP_METADATA " +
"WHERE SP_ID = ?";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,7 @@ CREATE TABLE IF NOT EXISTS IDP_AUTHENTICATOR (
NAME VARCHAR(255) NOT NULL,
IS_ENABLED CHAR (1) DEFAULT '1',
DISPLAY_NAME VARCHAR(255),
DEFINED_BY VARCHAR(255) NOT NULL,
PRIMARY KEY (ID),
UNIQUE (TENANT_ID, IDP_ID, NAME),
FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.wso2.carbon.identity.application.authentication.framework.exception.LogoutFailedException;
import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatorData;
import org.wso2.carbon.identity.application.common.model.Property;
import org.wso2.carbon.identity.base.IdentityConstants;

import java.io.Serializable;
import java.util.List;
Expand Down Expand Up @@ -171,4 +172,13 @@ default String getI18nKey() {
return StringUtils.EMPTY;
}

/**
* Get the authenticator type. Default value will be SYSTEM.
*
* @return Authenticator Type.
*/
default IdentityConstants.DefinedByType getDefinedByType() {

return IdentityConstants.DefinedByType.SYSTEM;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
import org.wso2.carbon.identity.application.common.model.Property;
import org.wso2.carbon.identity.application.common.model.RequestPathAuthenticatorConfig;
import org.wso2.carbon.identity.application.mgt.ApplicationManagementService;
import org.wso2.carbon.identity.base.IdentityConstants;
import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataManagementService;
import org.wso2.carbon.identity.configuration.mgt.core.ConfigurationManager;
import org.wso2.carbon.identity.core.handler.HandlerComparator;
Expand Down Expand Up @@ -506,6 +507,7 @@ protected void setAuthenticator(ApplicationAuthenticator authenticator) {
localAuthenticatorConfig.setProperties(configProperties);
localAuthenticatorConfig.setDisplayName(authenticator.getFriendlyName());
localAuthenticatorConfig.setTags(getTags(authenticator));
localAuthenticatorConfig.setDefinedByType(IdentityConstants.DefinedByType.SYSTEM);
AuthenticatorConfig fileBasedConfig = getAuthenticatorConfig(authenticator.getName());
localAuthenticatorConfig.setEnabled(fileBasedConfig.isEnabled());
ApplicationAuthenticatorService.getInstance().addLocalAuthenticator(localAuthenticatorConfig);
Expand All @@ -515,6 +517,7 @@ protected void setAuthenticator(ApplicationAuthenticator authenticator) {
federatedAuthenticatorConfig.setProperties(configProperties);
federatedAuthenticatorConfig.setDisplayName(authenticator.getFriendlyName());
federatedAuthenticatorConfig.setTags(getTags(authenticator));
federatedAuthenticatorConfig.setDefinedByType(IdentityConstants.DefinedByType.SYSTEM);
ApplicationAuthenticatorService.getInstance().addFederatedAuthenticator(federatedAuthenticatorConfig);
} else if (authenticator instanceof RequestPathApplicationAuthenticator) {
RequestPathAuthenticatorConfig reqPathAuthenticatorConfig = new RequestPathAuthenticatorConfig();
Expand All @@ -524,6 +527,7 @@ protected void setAuthenticator(ApplicationAuthenticator authenticator) {
reqPathAuthenticatorConfig.setTags(getTags(authenticator));
AuthenticatorConfig fileBasedConfig = getAuthenticatorConfig(authenticator.getName());
reqPathAuthenticatorConfig.setEnabled(fileBasedConfig.isEnabled());
reqPathAuthenticatorConfig.setDefinedByType(IdentityConstants.DefinedByType.SYSTEM);
ApplicationAuthenticatorService.getInstance().addRequestPathAuthenticator(reqPathAuthenticatorConfig);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ CREATE TABLE IF NOT EXISTS IDP_AUTHENTICATOR (
NAME VARCHAR(255) NOT NULL,
IS_ENABLED CHAR (1) DEFAULT '1',
DISPLAY_NAME VARCHAR(255),
DEFINED_BY VARCHAR(255) NOT NULL,
PRIMARY KEY (ID),
UNIQUE (TENANT_ID, IDP_ID, NAME),
FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ CREATE TABLE IF NOT EXISTS IDP_AUTHENTICATOR (
NAME VARCHAR(255) NOT NULL,
IS_ENABLED CHAR (1) DEFAULT '1',
DISPLAY_NAME VARCHAR(255),
DEFINED_BY VARCHAR(255) NOT NULL,
PRIMARY KEY (ID),
UNIQUE (TENANT_ID, IDP_ID, NAME),
FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -619,4 +619,13 @@ public static class APIResponse {

public static final String SET_ACCOUNT_LOCK_AUTH_FAILURE_REASON = "APIResponse.SetAccountLockAuthFailureReason";
}

/**
* The Authentication Type - SYSTEM: system define authenticator, CUSTOM: user defined authentication extension.
*/
public enum DefinedByType {

SYSTEM,
USER
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1148,6 +1148,8 @@ private FederatedAuthenticatorConfig[] getFederatedAuthenticatorConfigs(
}

authnConfig.setDisplayName(rs.getString("DISPLAY_NAME"));
authnConfig.setDefinedByType(IdentityConstants.DefinedByType.valueOf(
rs.getString("DEFINED_BY")));

if (defaultAuthName != null && authnConfig.getName().equals(defaultAuthName)) {
federatedIdp.getDefaultAuthenticatorConfig().setDisplayName(authnConfig.getDisplayName());
Expand Down Expand Up @@ -1424,6 +1426,7 @@ public void addFederatedAuthenticatorConfig(FederatedAuthenticatorConfig authnCo
}
prepStmt1.setString(4, authnConfig.getName());
prepStmt1.setString(5, authnConfig.getDisplayName());
prepStmt1.setString(6, authnConfig.getDefinedByType().toString());
prepStmt1.execute();

int authnId = getAuthenticatorIdentifier(dbConnection, idpId, authnConfig.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ public static class SQLQueries {
public static final String GET_IDP_ID_BY_NAME_SQL = "SELECT ID "
+ "FROM IDP WHERE TENANT_ID=? AND NAME=?";

public static final String GET_ALL_IDP_AUTH_SQL = "SELECT ID, NAME, IS_ENABLED, DISPLAY_NAME FROM " +
public static final String GET_ALL_IDP_AUTH_SQL = "SELECT ID, NAME, IS_ENABLED, DISPLAY_NAME, DEFINED_BY FROM " +
"IDP_AUTHENTICATOR WHERE IDP_ID = ?";

public static final String GET_IDP_AUTH_SQL = "SELECT ID FROM IDP_AUTHENTICATOR WHERE IDP_ID = ? AND NAME = ?";
Expand Down Expand Up @@ -357,7 +357,7 @@ public static class SQLQueries {
public static final String TRUSTED_TOKEN_ISSUER_FILTER_SQL = "IDP_METADATA.\"VALUE\" = 'true' AND ";

public static final String ADD_IDP_AUTH_SQL = "INSERT INTO IDP_AUTHENTICATOR " +
"(IDP_ID, TENANT_ID, IS_ENABLED, NAME, DISPLAY_NAME) VALUES (?,?,?,?,?)";
"(IDP_ID, TENANT_ID, IS_ENABLED, NAME, DISPLAY_NAME, DEFINED_BY) VALUES (?,?,?,?,?,?)";

public static final String DELETE_IDP_AUTH_SQL = "DELETE FROM IDP_AUTHENTICATOR WHERE IDP_ID=? AND NAME=?";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.wso2.carbon.identity.application.common.model.Property;
import org.wso2.carbon.identity.application.common.model.ProvisioningConnectorConfig;
import org.wso2.carbon.identity.application.common.model.RoleMapping;
import org.wso2.carbon.identity.base.IdentityConstants;
import org.wso2.carbon.identity.core.model.ExpressionNode;
import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil;
import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
Expand Down Expand Up @@ -588,6 +589,7 @@ public Object[][] addIdPData() {
federatedAuthenticatorConfig.setDisplayName("DisplayName1");
federatedAuthenticatorConfig.setName("Name");
federatedAuthenticatorConfig.setEnabled(true);
federatedAuthenticatorConfig.setDefinedByType(IdentityConstants.DefinedByType.SYSTEM);
Property property1 = new Property();
property1.setName("Property1");
property1.setValue("value1");
Expand Down Expand Up @@ -1104,6 +1106,7 @@ public Object[][] updateIdPData() {
federatedAuthenticatorConfig.setDisplayName("DisplayName1");
federatedAuthenticatorConfig.setName("Name");
federatedAuthenticatorConfig.setEnabled(true);
federatedAuthenticatorConfig.setDefinedByType(IdentityConstants.DefinedByType.SYSTEM);
Property property1 = new Property();
property1.setName("Property1");
property1.setValue("value1");
Expand Down Expand Up @@ -1161,6 +1164,7 @@ public Object[][] updateIdPData() {
newFederatedAuthenticatorConfig.setDisplayName("DisplayName1New");
newFederatedAuthenticatorConfig.setName("Name");
newFederatedAuthenticatorConfig.setEnabled(true);
newFederatedAuthenticatorConfig.setDefinedByType(IdentityConstants.DefinedByType.SYSTEM);
Property property1New = new Property();
property1New.setName("Property1New");
property1New.setValue("value1New");
Expand Down Expand Up @@ -1733,6 +1737,7 @@ private void addTestIdps() throws IdentityProviderManagementException {
federatedAuthenticatorConfig.setDisplayName("DisplayName1");
federatedAuthenticatorConfig.setName("Name");
federatedAuthenticatorConfig.setEnabled(true);
federatedAuthenticatorConfig.setDefinedByType(IdentityConstants.DefinedByType.SYSTEM);
Property property1 = new Property();
property1.setName("Property1");
property1.setValue("value1");
Expand Down Expand Up @@ -1864,6 +1869,7 @@ private void addTestTrustedTokenIssuers() throws IdentityProviderManagementExcep
federatedAuthenticatorConfig.setDisplayName("DisplayName1");
federatedAuthenticatorConfig.setName("Name");
federatedAuthenticatorConfig.setEnabled(true);
federatedAuthenticatorConfig.setDefinedByType(IdentityConstants.DefinedByType.SYSTEM);
Property property1 = new Property();
property1.setName("Property1");
property1.setValue("value1");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,7 @@ CREATE TABLE IF NOT EXISTS IDP_AUTHENTICATOR (
NAME VARCHAR(255) NOT NULL,
IS_ENABLED CHAR (1) DEFAULT '1',
DISPLAY_NAME VARCHAR(255),
DEFINED_BY VARCHAR(255) NOT NULL,
PRIMARY KEY (ID),
UNIQUE (TENANT_ID, IDP_ID, NAME),
FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,7 @@ CREATE TABLE IDP_AUTHENTICATOR (
NAME VARCHAR(255) NOT NULL,
IS_ENABLED CHAR (1) DEFAULT '1',
DISPLAY_NAME VARCHAR(255),
DEFINED_BY VARCHAR(255) NOT NULL,
PRIMARY KEY (ID),
UNIQUE (TENANT_ID, IDP_ID, NAME),
FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,7 @@ CREATE TABLE IF NOT EXISTS IDP_AUTHENTICATOR (
NAME VARCHAR(255) NOT NULL,
IS_ENABLED CHAR (1) DEFAULT '1',
DISPLAY_NAME VARCHAR(255),
DEFINED_BY VARCHAR(255) NOT NULL,
PRIMARY KEY (ID),
UNIQUE (TENANT_ID, IDP_ID, NAME),
FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,7 @@ CREATE TABLE IDP_AUTHENTICATOR (
NAME VARCHAR(255) NOT NULL,
IS_ENABLED CHAR (1) DEFAULT '1',
DISPLAY_NAME VARCHAR(255),
DEFINED_BY VARCHAR(255) NOT NULL,
PRIMARY KEY (ID),
UNIQUE (TENANT_ID, IDP_ID, NAME),
FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,7 @@ CREATE TABLE IF NOT EXISTS IDP_AUTHENTICATOR (
NAME VARCHAR(255) NOT NULL,
IS_ENABLED CHAR(1) DEFAULT '1',
DISPLAY_NAME VARCHAR(255),
DEFINED_BY VARCHAR(255) NOT NULL,
PRIMARY KEY (ID),
UNIQUE (TENANT_ID, IDP_ID, NAME),
FOREIGN KEY (IDP_ID) REFERENCES IDP (ID)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,7 @@ CREATE TABLE IF NOT EXISTS IDP_AUTHENTICATOR (
NAME VARCHAR(255) NOT NULL,
IS_ENABLED CHAR (1) DEFAULT '1',
DISPLAY_NAME VARCHAR(255),
DEFINED_BY VARCHAR(255) NOT NULL,
PRIMARY KEY (ID),
UNIQUE (TENANT_ID, IDP_ID, NAME),
FOREIGN KEY (IDP_ID) REFERENCES IDP(ID) ON DELETE CASCADE
Expand Down
Loading

0 comments on commit 36a46fc

Please sign in to comment.