Friday, December 28, 2018

Adaptive Authentication with WSO2 Identity Server

In one of my previous blog post Conditional Authentication! What and Why, I explained the necessity to "adopt" the authentication flow according to the user’s risk profile or the situation.

In this post, let's see how to implement a use case with WSO2 Identity Server 5.7.0. We will use the example of the user management system from my previous blog post. For convenience, I'll add the use case below.
There is a user management system in your company. All the users can login to the system and manage their profile and change their password. But if you are an admin user in the system, you can add users, delete users, reset passwords of other users and do all the risky tasks in the system. While all the users can login to the system with just username and password, if you are an admin user, you have to enter your “Time-based One Time Password” (TOTP).
First, download the WSO2 Identity Server 5.7.0 from the official download page and start it if you haven't done it already. Now configure the application in Identity Server and add the inbound authentication configuration as described in this documentation.

Now we are ready to configure the adaptive authentication for this service provider. Go to the "Local & Outbound Authentication Configuration" section of the service provider configuration and click on the "Advanced Authentication" button. You will be redirected to below page.


Expand the "Script Based Adaptive Authentication" section and you will be able to see a list of templates available.


From the available template list, we will be using the "Role-Based" template. To use it click on the "+" next to "Role-Based" label. Now the editor will be populated with below template.

// Role-Based from Template...

// This script will step up authentication for any user belonging
// to one of the given roles
// If the user has any of the below roles, authentication will be stepped up
var rolesToStepUp = ['admin', 'manager'];

function onLoginRequest(context) {
    executeStep(1, {
        onSuccess: function (context) {
            // Extracting authenticated subject from the first step
            var user = context.currentKnownSubject;
            // Checking if the user is assigned to one of the given roles
            var hasRole = hasAnyOfTheRoles(user, rolesToStepUp);
            if (hasRole) {
                Log.info(user.username + ' Has one of Roles: ' + rolesToStepUp.toString());
                executeStep(2);
            }
        }
    });
}

// End of Role-Based.......

The script is pretty much self-explanatory. First, we execute step 1, which is configured to basic authentication in the "Authentication Step Configuration" section. The after successful execution of the first step, we check whether the logged in user has the "admin" or the "manager" roles. If so, we execute step 2, which is configured to either TOTP or FIDO authentication.

Click on the "Update" button and then save the service provider configuration.

Now we are ready to test the adaptive authentication.
First, try to log in as an admin user. After entering the admin credentials, the user will be prompted for a second factor.
Next, try to log in as a normal user (a user that don't have admin or the manager roles). Just by entering the username and the password, the user will be able to log in.

So now we have implemented the scenario successfully. Identity Server has a set of pre-defined templates for adaptive authentication. Explore them and see the use cases you can implement. Adaptive Authentication JS API Reference document contains more information about each API and objects used in the authentication script. Using them you can implement many more use cases.

Centering Widgets with ConstraintLayout in Android

Placing widgets in the exact place you want on an Android layout is a difficult task. With ConstraintLayout, you can achieve this relatively easy. In a ConstraintLayout, you can use below configuration to center the button in the window.

<Button
        android:id="@+id/testbutton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="Test"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

Interestingly this will add the button in the center of the window like below.

If we consider horizontal positioning, we have configured to place the left of the button to the left of the parent, (which refers to the parent container, i.e. the ConstraintLayout) and place the right of the button to the right of the parent. This is an impossible layout unless the width of the button is equal to the width of the parent. ConstraintLayout handles this by placing the button in the center of the parent, like pulling the button by the two constraints resulting to make the button keep in the middle. Same goes with the vertical positioning.






But if you add a bias to position the widget in favor of one side. For example, if we add below bias to the above positioning, the result will be as below image.

<Button
        ...
        app:layout_constraintVertical_bias="0.8" />

If we add the bias as 0.8, the widget will be placed after 80% of the container.

Tuesday, November 20, 2018

Conditional Authentication! What and Why

When you read the term “Conditional Authentication” the first thing that comes to your mind is what is “Conditional Authentication”?

Conditional authentication is a type of multi-factor authentication that can change or “adopt” the authentication flow or steps according to the user’s risk profile or the situation.

Still confusing? Let’s take a few examples.
  1. There is a banking application. Viewing the balance in your bank account is relatively less risky, right? So its enough to authenticate with just username and password. But if you want to make a transaction, it's relatively high risk. So you have to authenticate with your username and password and then maybe with SMS One-Time-Password (OTP).
  2. There is a user management system in your company. All the users can login to the system and manage their profile and change their password. But if you are an admin user in the system, you can add users, delete users, reset passwords of other users and do all the risky tasks in the system. While all the users can login to the system with just username and password, if you are an admin user, you have to enter your “Time-based One Time Password” (TOTP).
  3. There is a financial management application in your company. You are a financial officer in the company and while working in the office, you have to login to the application always. If you are working from the office, it is sufficient for you to enter the username and password only to login to the application. But if you are trying to login to the application from outside of the office network, you have to enter One-Time-Password sent to you over the email.
We can demonstrate a simple scenario with below flow chart.


Now you can ask, why do we need conditional authentication? We can just have the additional authentication steps statically configured for all the users and all the operations. Well, mostly its related to user experience. Let’s take the above examples again.
  1. When checking the account balance from a banking application, if you always have to enter your SMS OTP, you would be frustrated over time, right?
  2. When you need to update the profile picture, if you have to enter a TOTP, you would be frustrated, right?
  3. If you work in the office and you need to authenticate with two steps always, you would be frustrated over time, right?
So we can summarize the idea of conditional authentication like below.
Conditional authentication is a type of multi-factor authentication that can change or “adopt” the authentication flow or steps according to user’s risk profile or the situation without compromising user experience.

Sunday, July 24, 2016

WSO2 IDP for Spring SAML Service Provider

Today we are going to see how to add WSO2 Identity Server as an IDP to a Spring application. We are going to do this using the Spring SAML sample provided here. You can download one of the releases of Spring SAML sample from here. I have tested this with 1.0.2.RELEASE.

Sample Application Configurations

Create SAML metadata file like below (wso2.xml). You have to change the entityID, samlsso url and X509Certificate according to your installation of the Identity Server. Below values is the default. You can use this blog post to find the X509Certificate of your Identity Server deployment.

<?xml version="1.0" encoding="UTF-8"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="localhost" validUntil="2026-05-16T21:51:14.927Z">
   <md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
      <md:KeyDescriptor use="signing">
         <ds:KeyInfo>
            <ds:X509Data>
                  <ds:X509Certificate>
MIICNTCCAZ6gAwIBAgIES343gjANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJV
UzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxDTALBgNVBAoM
BFdTTzIxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xMDAyMTkwNzAyMjZaFw0zNTAy
MTMwNzAyMjZaMFUxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwN
TW91bnRhaW4gVmlldzENMAsGA1UECgwEV1NPMjESMBAGA1UEAwwJbG9jYWxob3N0
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCUp/oV1vWc8/TkQSiAvTousMzO
M4asB2iltr2QKozni5aVFu818MpOLZIr8LMnTzWllJvvaA5RAAdpbECb+48FjbBe
0hseUdN5HpwvnH/DW8ZccGvk53I6Orq7hLCv1ZHtuOCokghz/ATrhyPq+QktMfXn
RS4HrKGJTzxaCcU7OQIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcN
AQEFBQADgYEAW5wPR7cr1LAdq+IrR44iQlRG5ITCZXY9hI0PygLP2rHANh+PYfTm
xbuOnykNGyhM6FjFLbW2uZHQTY1jMrPprjOrmyK5sjJRO4d1DeGHT/YnIjs9JogR
Kv4XHECwLtIVdAbIdWHEtVZJyMSktcyysFcvuhPQK8Qc/E/Wq8uHSCo=
                  </ds:X509Certificate>
            </ds:X509Data>
         </ds:KeyInfo>
      </md:KeyDescriptor>
      <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://localhost:9443/samlsso"/>
      <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://localhost:9443/samlsso"/>
      <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
      <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://localhost:9443/samlsso"/>
      <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://localhost:9443/samlsso"/>
   </md:IDPSSODescriptor>
</md:EntityDescriptor>

Copy the created wso2.xml metadata file to <SAMPLE_HOME>/sample/src/main/resources/metadata directory. Now you need to refer this metadata file from the application. To do this you need to open <SAMPLE_HOME>/sample/src/main/webapp/WEB-INF/securityContext.xml file and find the bean with id "metadata". In the list add below to include Identity Server as an identity provider.


<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
    <constructor-arg>
        <bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
            <constructor-arg>
                <bean class="java.util.Timer"/>
            </constructor-arg>
            <constructor-arg>
                <bean class="org.opensaml.util.resource.ClasspathResource">
                    <constructor-arg value="/metadata/wso2.xml"/>
                </bean>
            </constructor-arg>
            <property name="parserPool" ref="parserPool"/>
        </bean>
    </constructor-arg>
    <constructor-arg>
        <bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
        </bean>
    </constructor-arg>
</bean>

Identity Server Configurations

Create a service provider. Go to Inbound Authentication Configuration and SAML2 Web SSO Configuration. Add the below configurations.
  • Issuer: http://localhost:8080/spring-security-saml2-sample/saml/metadata
  • Assertion Consumer URLs: http://localhost:8080/spring-security-saml2-sample/saml/SSO
  • Enable Response Signing.
  • Enable Single Logout.
  • SLO Response URL: http://localhost:8080/spring-security-saml2-sample/saml/SingleLogout
  • Enable Attribute Profile.
  • Include Attributes in the Response Always.
It will look like below.


Working with the sample.

Go to <SAMPLE_HOME>/sample from the terminal and run below command to build the project.
                 mvn package
Now run the below command to start the tomcat server and start the application.
                 mvn tomcat7:run

Go to the below url.
                 http://localhost:8080/spring-security-saml2-sample
You will be redirected to below page which is the index page of the sample application.


You can see the localhost as an IDP here. Its because the entityID of the metadata file we created is localhost. From the list select localhost and click "Start single sign-on" button. Now you will be redirected to the login page of the Identity Server.


Insert the username and password in the Identity Server and click Sign In. You will be redirected back to the sample application. Now you are logged in to the application and you can see the details of the authenticated user.


Wednesday, May 4, 2016

Adding custom admin service to run LDAP search filter from WSO2 Identity Server

From this post I'm going to explain how to create a custom admin service WSO2 Identity Server. For this I am using WSO2 Identity Server 5.0.0 with Service Pack 1 installed and going to create a admin service to run a LDAP search filter and get the results from the ReadWriteLDAPUserStoreManager.

First you need to created a maven project. Use the bellow pom file.

<?xml version="1.0" encoding="utf-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>org.wso2.carbon</groupId>
    <artifactId>custom.admin.service</artifactId>
    <version>4.2.3</version>
    <packaging>bundle</packaging>
    <name>WSO2 Carbon - OAuth</name>
    <description>A custom wso2 products or solution</description>
    <url>http://wso2.org</url>

    <dependencies>
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.logging</artifactId>
            <version>4.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.core</artifactId>
            <version>4.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.wso2.carbon</groupId>
            <artifactId>org.wso2.carbon.user.core</artifactId>
            <version>4.2.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-scr-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Bundle-SymbolicName>
                            ${project.artifactId}
                        </Bundle-SymbolicName>
                        <Private-Package>
                            custom.admin.service.internal
                        </Private-Package>
                        <Import-Package>
                            org.apache.commons.logging.*; version="1.0.4",
                            *;resolution:=optional
                        </Import-Package>
                        <Export-Package>
                            custom.admin.service,
                        </Export-Package>
                        <Embed-Dependency>
                        </Embed-Dependency>
                        <DynamicImport-Package>*</DynamicImport-Package>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>wso2-nexus</id>
            <name>WSO2 internal Repository</name>
            <url>http://maven.wso2.org/nexus/content/groups/wso2-public/</url>
            <releases>
                <enabled>true</enabled>
                <updatePolicy>daily</updatePolicy>
                <checksumPolicy>ignore</checksumPolicy>
            </releases>
        </repository>
    </repositories>
</project>

You need to write a custom userstore manager to be able to run you LDAP search filter. This can easily done by extending ReadWriteLDAPUserStoreManager. Our new userstore manager is CustomUserStoreManager. We need to add the bellow constructor to our custom userstore manager.


public CustomUserStoreManager(org.wso2.carbon.user.api.RealmConfiguration realmConfig,
                                  Map<String, Object> properties,
                                  ClaimManager claimManager,
                                  ProfileConfigurationManager profileManager,
                                  UserRealm realm, Integer tenantId)
            throws UserStoreException {

        super(realmConfig, properties, claimManager, profileManager, realm, tenantId);

}

Then we need to add a method to run the search filter on LDAP.

public String[] runSearchFilter(String filter) throws UserStoreException {

    List<String> values = new ArrayList<String>();
    String userPropertyName = realmConfig
            .getUserStoreProperty(LDAPConstants.USER_NAME_ATTRIBUTE);

    String searchFilter = filter;

    DirContext dirContext = this.connectionSource.getContext();
    NamingEnumeration<?> answer = null;
    NamingEnumeration<?> attrs = null;
    try {
        answer = this.searchForUser(searchFilter, new String[] { userPropertyName }, dirContext);
        while (answer.hasMoreElements()) {
            SearchResult sr = (SearchResult) answer.next();
            Attributes attributes = sr.getAttributes();
            if (attributes != null) {
                Attribute attribute = attributes.get(userPropertyName);
                if (attribute != null) {
                    StringBuffer attrBuffer = new StringBuffer();
                    for (attrs = attribute.getAll(); attrs.hasMore();) {
                        String attr = (String) attrs.next();
                        if (attr != null && attr.trim().length() > 0) {
                            attrBuffer.append(attr + ",");
                        }
                    }
                    String propertyValue = attrBuffer.toString();
                    // Length needs to be more than one for a valid
                    // attribute, since we
                    // attach ",".
                    if (propertyValue != null && propertyValue.trim().length() > 1) {
                        propertyValue = propertyValue.substring(0, propertyValue.length() - 1);
                        values.add(propertyValue);
                    }
                }
            }
        }

    } catch (NamingException e) {
        throw new UserStoreException(e.getMessage(), e);
    } finally {
        // close the naming enumeration and free up resources
        JNDIUtil.closeNamingEnumeration(attrs);
        JNDIUtil.closeNamingEnumeration(answer);
        // close directory context
        JNDIUtil.closeContext(dirContext);
    }
    return values.toArray(new String[values.size()]);
}

Now you need to register the newly created userstore manager as a service. For this you need to create private package and add service component class. This private package need to be added to pom file as done above. Bellow is a sample code snippet.


import custom.admin.service.CustomUserStoreManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.service.component.ComponentContext;
import org.wso2.carbon.user.api.UserStoreManager;

/**
 * @scr.component name="custom.user.store.manager.dscomponent" immediate=true
 */
public class CustomUserStoreMgtDSComponent {
    private static Log log = LogFactory.getLog(CustomUserStoreMgtDSComponent.class);

    protected void activate(ComponentContext ctxt) {

        CustomUserStoreManager customUserStoreManager = new CustomUserStoreManager();
        ctxt.getBundleContext().registerService(UserStoreManager.class.getName(), customUserStoreManager, null);
        log.info("CustomUserStoreManager bundle activated successfully..");
    }

    protected void deactivate(ComponentContext ctxt) {
        if (log.isDebugEnabled()) {
            log.debug("Custom User Store Manager is deactivated ");
        }
    }
}

Now we need to create the admin service class. It retrieves the primary userstore manager for the super tenant and cast it to our new custom userstore manager and invoke our new method.

import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.core.AbstractAdmin;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.user.api.UserRealm;
import org.wso2.carbon.user.core.common.AbstractUserStoreManager;
import org.wso2.carbon.user.core.service.RealmService;

public class CustomAdminService extends AbstractAdmin {

    protected Log log = LogFactory.getLog(CustomAdminService.class);

    public String[] filterUsers(String filter) throws Exception {
        //get super tenant context and get realm service which is an osgi service
        RealmService realmService = (RealmService)
                PrivilegedCarbonContext.getThreadLocalCarbonContext().getOSGiService(RealmService.class);
        if (realmService == null) {
            String error = "Can not obtain carbon realm service..";
            throw new Exception(error);
        }

        int tenantId = -1234;
        UserRealm userRealm = realmService.getTenantUserRealm(tenantId);
        if (userRealm == null || !(userRealm instanceof org.wso2.carbon.user.core.UserRealm)) {
            String error = "Can not obtain user realm for tenant carbon.super.";
            throw new Exception(error);
        }
        AbstractUserStoreManager userStoreManager = (AbstractUserStoreManager) userRealm.getUserStoreManager();
        if (userStoreManager instanceof CustomUserStoreManager) {
            return ((CustomUserStoreManager)userStoreManager).runSearchFilter(filter);
        }else {
            throw new Exception("Operation not supported");
        }
    }
}

Now we need to create services.xml. It should be in <project home>/src/main/resources/META-INF/ directory. Bellow is a sample services.xml file. This file is used to map the service name to the service class, add permission to the service and etc.


<serviceGroup>
 <service name="CustomAdminService" scope="transportsession">
     <transports>
            <transport>https</transport>
        </transports>
  <schema schemaNamespace="http://org.apache.axis2/xsd" elementFormDefaultQualified="true" />
  <description>OAuth administration related functionality.</description>
  <parameter name="ServiceClass">custom.admin.service.CustomAdminService</parameter>
  <parameter name="AuthorizationAction" locked="false">/permission/admin/manage</parameter>
        <parameter name="adminService" locked="true">true</parameter>
    </service>
 <parameter name="hiddenService" locked="true">true</parameter>
</serviceGroup>

Now our custom userstore manager is ready. Build the project using maven and add it to the <CARBON_HOME>/repository/components/dropins/ directory.

Finally you need to change the userstore manager class name in user-mgt.xml file. So we need to change bellow line in user-mgt.xml

<UserStoreManager class="org.wso2.carbon.user.core.ldap.ReadWriteLDAPUserStoreManager">

to

<UserStoreManager class="custom.admin.service.CustomUserStoreManager">

Now our custom component is ready. To view the wsdl of the service we need to change HideAdminServiceWSDLs in carbon.xml to true. Finally start the Identity Server and go to the bellow url to view the wsdl of the service.

https://localhost:9443/services/CustomAdminService?wsdl

You can invoke the admin service using SOAP client. Bellow is a sample SOAP request for the service. We need admin privileges to invoke this service.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://org.apache.axis2/xsd">
   <soapenv:Header/>
   <soapenv:Body>
      <xsd:filterUsers>
         <!--Optional:-->
         <xsd:filter>(uid=user102)</xsd:filter>
      </xsd:filterUsers>
   </soapenv:Body>
</soapenv:Envelope>

You can pass any LDAP search filter to this service and it will return all the users that matches the search filter.

Source code for the service and userstore manager is available at https://github.com/madurangasiriwardena/custom-admin-service

Sunday, September 20, 2015

Dropping foreign key constraints from database

If you have explicitly stated the name of a constraint in a database, you can easily drop the constraint using the name specified. But if you haven't specified a name for a constraint at the time of the construction of the table, database engine will assign a name automatically and you will have to find the constraint name from the system tables of the database.

From this post we are going to see how to find the name of a foreign key constraint and drop it in a single script for different database systems.

1. H2

CREATE ALIAS IF NOT EXISTS DROP_FK AS $$ void executeSql(Connection conn, String sql)
throws SQLException { conn.createStatement().executeUpdate(sql); } $$;

call drop_fk('ALTER TABLE TABLE_NAME DROP CONSTRAINT ' ||
(SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINTS 
WHERE TABLE_NAME = 'TABLE_NAME' AND COLUMN_LIST  = 'COLUMN_NAME'));

DROP ALIAS IF EXISTS DROP_FK;

2. Microsoft SQL Server

DECLARE @COMMAND NVARCHAR(200);

SELECT @COMMAND= 'ALTER TABLE TABLE_NAME DROP CONSTRAINT ' + RC.CONSTRAINT_NAME + ';'
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KF ON RC.CONSTRAINT_NAME = KF.CONSTRAINT_NAME
JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KP ON RC.UNIQUE_CONSTRAINT_NAME = KP.CONSTRAINT_NAME
WHERE KF.TABLE_NAME = 'TABLE_NAME';

EXEC (@COMMAND);

3. MySQL

SET @databasename = "sample";

SELECT CONCAT("ALTER TABLE TABLE_NAME DROP FOREIGN KEY ",constraint_name)
INTO @sqlst
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
where TABLE_SCHEMA = @databasename and TABLE_NAME = "TABLE_NAME"
and referenced_column_name is not NULL limit 1;

PREPARE stmt FROM @sqlst;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @sqlstr = NULL;

4. Oracle

declare
  con_name varchar2(100);
  command varchar2(200);
  databasename VARCHAR2(100);
BEGIN
select constraint_name into con_name from all_constraints where table_name='TABLE_NAME' AND owner=databasename AND 
constraint_type = 'R';

    if TRIM(con_name) is not null
    then
      command := 'ALTER TABLE TABLE_NAME DROP CONSTRAINT ' || con_name;
      dbms_output.Put_line(command);
      execute immediate command;
    end if;

    exception
    when NO_DATA_FOUND
    then
    dbms_output.Put_line('Foreign key not found');
END;

5. PostgreSQL


DO $$ DECLARE con_name varchar(200); 
BEGIN 

SELECT 'ALTER TABLE table_name DROP CONSTRAINT ' || tc .constraint_name || ';' INTO con_name 
FROM information_schema.table_constraints AS tc 
JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name 
JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name 
WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = 'table_name' AND kcu.column_name = 'column_name';
 
EXECUTE con_name; 
END $$;

If you know the basic syntax of the sql, you can customize them according to your requirements.