<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[DevKata Dojo]]></title><description><![CDATA[Dedicated to sharing insights, tips, and experiences from my journey through the world of software development. I'm a passionate developer constantly exploring ]]></description><link>https://devkata.joergeous.one</link><generator>RSS for Node</generator><lastBuildDate>Fri, 09 Oct 2026 01:25:38 GMT</lastBuildDate><atom:link href="https://devkata.joergeous.one/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Setup OCI Function Development on Windows with WSL2]]></title><description><![CDATA[Install WSL2 - Ubuntu

Open the Microsoft Store.

Search for Ubuntu 24.04

Install


Launch Ubuntu terminal

Search for "Ubuntu" in your apps.

On the initial launch, you'll be prompted to create a username and password. Set these and remember them.
...]]></description><link>https://devkata.joergeous.one/setup-oci-function-development-on-windows-with-wsl2</link><guid isPermaLink="true">https://devkata.joergeous.one/setup-oci-function-development-on-windows-with-wsl2</guid><category><![CDATA[oci-functions]]></category><dc:creator><![CDATA[Joe Ngo]]></dc:creator><pubDate>Sun, 18 May 2025 16:25:30 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-install-wsl2-ubuntu">Install WSL2 - Ubuntu</h1>
<ol>
<li><p>Open the Microsoft Store.</p>
</li>
<li><p>Search for Ubuntu 24.04</p>
</li>
<li><p>Install</p>
</li>
</ol>
<h2 id="heading-launch-ubuntu-terminal">Launch Ubuntu terminal</h2>
<ol>
<li><p>Search for "Ubuntu" in your apps.</p>
</li>
<li><p>On the initial launch, you'll be prompted to create a username and password. Set these and remember them.</p>
</li>
<li><p>Execute the following commands:</p>
<pre><code class="lang-bash"> sudo apt update &amp;&amp; sudo apt full-upgrade -y
</code></pre>
</li>
</ol>
<h2 id="heading-install-podman-docker">Install podman-docker</h2>
<pre><code class="lang-bash">sudo apt install podman-docker
sudo touch /etc/containers/nodocker
</code></pre>
<h2 id="heading-install-fn-cli">Install Fn CLI</h2>
<pre><code class="lang-bash">curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh
</code></pre>
<p>Set the Fn settings to use the Podman engine by adding or updating the following line:</p>
<pre><code class="lang-bash">vi ~/.fn/config.yaml
</code></pre>
<p>Add or update line:</p>
<pre><code class="lang-plaintext">container-enginetype: podman
</code></pre>
<h2 id="heading-install-oci-cli">Install OCI CLI</h2>
<pre><code class="lang-bash">sudo apt install python3-pip
pip3 install oci-cli --break-system-packages
</code></pre>
<h3 id="heading-create-bashprofile">Create .bash_profile</h3>
<pre><code class="lang-bash">vi ~/.bash_profile
</code></pre>
<p>Set the file contents as follows:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-built_in">source</span> ~/.bashrc
<span class="hljs-built_in">export</span> PATH=~/.<span class="hljs-built_in">local</span>/bin:<span class="hljs-variable">$PATH</span>
</code></pre>
<h3 id="heading-setup-config">Setup Config</h3>
<pre><code class="lang-bash">oci setup config
</code></pre>
<h3 id="heading-complete-fn-setup">Complete Fn Setup</h3>
<p>Follow steps 4 onwards in the <a target="_blank" href="https://docs.oracle.com/en-us/iaas/Content/Functions/Tasks/functionsquickstartlocalhost.htm">Functions QuickStart on Local Host</a>.</p>
<p>NOTES:</p>
<ul>
<li><p>Step 4.3 is optional. Set it if you're using a profile other than the DEFAULT profile in the OCI CLI config.</p>
</li>
<li><p>Step 5.3: you can get the tenancy namespace by executing <code>oci os ns get</code>.</p>
</li>
</ul>
<h1 id="heading-development-in-vs-code">Development in VS Code</h1>
<ol>
<li><p>Install the Remote Development extension in VS Code.</p>
</li>
<li><p>Connect to WSL</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[How to Move a Complete Git Repository]]></title><description><![CDATA[Moving a Git repository while preserving all branches, tags, and history requires careful handling. This guide covers the step-by-step process to migrate your repository safely.
Quick Method
For those familiar with Git, here's the quick version:
git ...]]></description><link>https://devkata.joergeous.one/how-to-move-a-complete-git-repository</link><guid isPermaLink="true">https://devkata.joergeous.one/how-to-move-a-complete-git-repository</guid><category><![CDATA[Git]]></category><dc:creator><![CDATA[Joe Ngo]]></dc:creator><pubDate>Thu, 23 Jan 2025 07:21:23 GMT</pubDate><content:encoded><![CDATA[<p>Moving a Git repository while preserving all branches, tags, and history requires careful handling. This guide covers the step-by-step process to migrate your repository safely.</p>
<h2 id="heading-quick-method">Quick Method</h2>
<p>For those familiar with Git, here's the quick version:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> --mirror <span class="hljs-variable">$ORIG_REPO_URL</span> temp-dir
<span class="hljs-built_in">cd</span> temp-dir
git remote rm origin
git remote add origin <span class="hljs-variable">$NEW_REPO_URL</span>
git push origin --all
git push --tags
</code></pre>
<h2 id="heading-detailed-steps">Detailed Steps</h2>
<ol>
<li><p><strong>Clone the Original Repository</strong></p>
<pre><code class="lang-bash"> git <span class="hljs-built_in">clone</span> --mirror <span class="hljs-variable">$ORIG_REPO_URL</span> temp-dir
</code></pre>
<p> The <code>--mirror</code> flag creates a bare repository, including all branches and tags.</p>
</li>
<li><p><strong>Navigate to Directory</strong></p>
<pre><code class="lang-bash"> <span class="hljs-built_in">cd</span> temp-dir
</code></pre>
</li>
<li><p><strong>Update Remote Configuration</strong></p>
<pre><code class="lang-bash"> git remote rm origin
 git remote add origin <span class="hljs-variable">$NEW_REPO_URL</span>
</code></pre>
<p> This step redirects the repository to its new destination.</p>
</li>
<li><p><strong>Push All Content</strong></p>
<pre><code class="lang-bash"> git push origin --all
 git push --tags
</code></pre>
<p> These commands transfer all branches and tags to the new location.</p>
</li>
</ol>
<h2 id="heading-verification">Verification</h2>
<p>After migration, verify:</p>
<ul>
<li><p>All branches are present in the new repository</p>
</li>
<li><p>Commit history is intact</p>
</li>
<li><p>Tags are properly transferred</p>
</li>
<li><p>Remote URLs are correctly configured</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Auditing IAM Policies Across Oracle Cloud Infrastructure Compartments]]></title><description><![CDATA[Oracle Cloud Infrastructure (OCI) uses a hierarchical compartment structure for resource organization and access control. Understanding and auditing your IAM policies across these compartments is crucial for maintaining proper security posture. In th...]]></description><link>https://devkata.joergeous.one/auditing-oci-iam-policies</link><guid isPermaLink="true">https://devkata.joergeous.one/auditing-oci-iam-policies</guid><category><![CDATA[OCI CLI]]></category><category><![CDATA[OCI IAM]]></category><dc:creator><![CDATA[Joe Ngo]]></dc:creator><pubDate>Tue, 12 Nov 2024 06:51:40 GMT</pubDate><content:encoded><![CDATA[<p>Oracle Cloud Infrastructure (OCI) uses a hierarchical compartment structure for resource organization and access control. Understanding and auditing your IAM policies across these compartments is crucial for maintaining proper security posture. In this guide, we'll explore a powerful shell script that helps you audit IAM policies across your entire OCI tenancy.</p>
<h2 id="heading-the-challenge">The Challenge</h2>
<p>Managing IAM policies across multiple compartments can become complex as your cloud infrastructure grows. Common challenges include:</p>
<ul>
<li><p>Tracking policy distributions across compartments</p>
</li>
<li><p>Identifying redundant or conflicting policies</p>
</li>
<li><p>Ensuring proper access controls at each level</p>
</li>
<li><p>Maintaining security compliance requirements</p>
</li>
</ul>
<h3 id="heading-console-limitations-in-policy-visibility">Console Limitations in Policy Visibility</h3>
<p>One significant challenge when using the OCI Console is the limited visibility of group permissions across compartments. Here's why this is problematic:</p>
<ol>
<li><p><strong>Scattered Policies</strong>: Permissions for a single group can be defined in multiple policies across different compartments. The console doesn't provide a unified view of all permissions assigned to a group.</p>
</li>
<li><p><strong>Inheritance Complexity</strong>: When compartments are nested, policies inherit from parent compartments. The console shows policies at each level separately, making it difficult to understand the cumulative permissions a group has.</p>
</li>
<li><p><strong>Dynamic Groups</strong>: When dynamic groups are involved, it becomes even more challenging to track permissions as they're based on instance principals and other dynamic attributes.</p>
</li>
<li><p><strong>Time-Consuming Manual Review</strong>: To understand a group's complete access level, administrators often need to:</p>
<ul>
<li><p>Navigate through multiple compartments</p>
</li>
<li><p>Open each policy individually</p>
</li>
<li><p>Mentally compile the permissions</p>
</li>
<li><p>Account for policy inheritance</p>
</li>
<li><p>Consider any conflicting or overlapping statements</p>
</li>
</ul>
</li>
</ol>
<p>This is where our script becomes particularly valuable, as it provides a comprehensive view of all policies across your tenancy in one output.</p>
<h2 id="heading-the-solution">The Solution</h2>
<p>We'll use a Bash script that leverages the OCI CLI to recursively traverse all compartments and list their associated IAM policies. This script can be run directly in OCI Cloud Shell, making it immediately accessible to all OCI users.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ol>
<li><p>OCI CLI installed and configured</p>
</li>
<li><p>Proper permissions to read compartments and IAM policies</p>
</li>
<li><p><code>jq</code> command-line JSON processor (pre-installed in Cloud Shell)</p>
</li>
</ol>
<h3 id="heading-the-script-breakdown">The Script Breakdown</h3>
<p><a target="_blank" href="https://raw.githubusercontent.com/ChiliJoe/blog-scripts/refs/heads/main/oci/audit_iam_policies.sh">Full script</a></p>
<p>Let's analyze the key components of our script:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Core function to list IAM policies for a single compartment</span>
<span class="hljs-function"><span class="hljs-title">list_compartment_iam_policies</span></span>() {
    <span class="hljs-built_in">local</span> compartment_id=<span class="hljs-variable">$1</span>
    oci iam policy list --compartment-id <span class="hljs-string">"<span class="hljs-variable">$compartment_id</span>"</span>
}
</code></pre>
<p>This function uses the OCI CLI to fetch all IAM policies for a given compartment ID.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Get the root compartment (tenancy) ID</span>
<span class="hljs-built_in">local</span> root_compartment=$(oci iam compartment list --all \
    --compartment-id-in-subtree <span class="hljs-literal">true</span> \
    --access-level ACCESSIBLE \
    --include-root \
    --raw-output \
    --query <span class="hljs-string">"data[?contains(\"id\",'tenancy')].id | [0]"</span>)
</code></pre>
<p>This command retrieves the tenancy OCID, which serves as our starting point for the recursive traversal.</p>
<pre><code class="lang-bash"><span class="hljs-keyword">function</span> <span class="hljs-function"><span class="hljs-title">_list_compartments</span></span>() {
    <span class="hljs-built_in">local</span> compartment_id=<span class="hljs-variable">$1</span>
    compartment_name=$(oci iam compartment get \
        --compartment-id <span class="hljs-string">"<span class="hljs-variable">$compartment_id</span>"</span> \
        --query <span class="hljs-string">"data.name"</span> \
        --raw-output)

    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Compartment: <span class="hljs-variable">$compartment_name</span> (<span class="hljs-variable">$compartment_id</span>)"</span>
    list_compartment_iam_policies <span class="hljs-string">"<span class="hljs-variable">$compartment_id</span>"</span>

    <span class="hljs-comment"># Recursive call for sub-compartments</span>
    <span class="hljs-keyword">for</span> sub_compartment_id <span class="hljs-keyword">in</span> $(oci iam compartment list \
        --compartment-id <span class="hljs-string">"<span class="hljs-variable">$compartment_id</span>"</span> \
        --all \
        --query <span class="hljs-string">"data[].id"</span> | jq -r <span class="hljs-string">'.[]'</span>); <span class="hljs-keyword">do</span>
        _list_compartments <span class="hljs-string">"<span class="hljs-variable">$sub_compartment_id</span>"</span>
    <span class="hljs-keyword">done</span>
}
</code></pre>
<p>This recursive function:</p>
<ol>
<li><p>Gets the compartment name for better readability</p>
</li>
<li><p>Lists IAM policies for the current compartment</p>
</li>
<li><p>Finds all sub-compartments</p>
</li>
<li><p>Recursively calls itself for each sub-compartment</p>
</li>
</ol>
<h2 id="heading-using-the-script">Using the Script</h2>
<ol>
<li><p>Save the script to a file (e.g., <code>audit_iam_policies.sh</code>)</p>
</li>
<li><p>Make it executable:</p>
<pre><code class="lang-bash"> chmod +x audit_iam_policies.sh
</code></pre>
</li>
<li><p>Run it:</p>
<pre><code class="lang-bash"> ./audit_iam_policies.sh
</code></pre>
</li>
</ol>
<h2 id="heading-output-format">Output Format</h2>
<p>The script produces output in the following format:</p>
<pre><code class="lang-bash">Compartment: root_compartment_name (ocid1.tenancy.oc1...)
[IAM policies <span class="hljs-keyword">for</span> root compartment]

Compartment: sub_compartment_name (ocid1.compartment.oc1...)
[IAM policies <span class="hljs-keyword">for</span> sub-compartment]
...
</code></pre>
<h2 id="heading-best-practices-for-policy-auditing">Best Practices for Policy Auditing</h2>
<p>When reviewing the output:</p>
<ol>
<li><p><strong>Policy Consolidation</strong>: Look for similar policies that could be combined at a higher level</p>
</li>
<li><p><strong>Least Privilege</strong>: Ensure policies grant only necessary permissions</p>
</li>
<li><p><strong>Documentation</strong>: Save the output for compliance and documentation purposes</p>
</li>
<li><p><strong>Regular Audits</strong>: Run this script periodically as part of your security routine</p>
</li>
</ol>
<h2 id="heading-enhancement-opportunities">Enhancement Opportunities</h2>
<p>The script can be enhanced to:</p>
<ol>
<li><p>Output in different formats (JSON, CSV, HTML)</p>
</li>
<li><p>Include policy statements analysis</p>
</li>
<li><p>Flag potentially risky permissions</p>
</li>
<li><p>Compare against a baseline configuration</p>
</li>
<li><p>Generate detailed reports with policy recommendations</p>
</li>
<li><p><strong>Group-Centric View</strong>: Add functionality to filter and display all policies affecting a specific group across compartments</p>
</li>
<li><p><strong>Permission Matrix</strong>: Generate a matrix showing which groups have what permissions across different compartments</p>
</li>
<li><p><strong>Policy Overlap Detection</strong>: Identify where multiple policies might be granting similar permissions to the same group</p>
</li>
</ol>
<h2 id="heading-pro-tips-for-policy-analysis">Pro Tips for Policy Analysis</h2>
<ol>
<li><p><strong>Group-Based Review</strong>:</p>
<ul>
<li>After running the script, use grep to filter policies for a specific group:</li>
</ul>
</li>
</ol>
<pre><code class="lang-bash">    ./audit_iam_policies.sh | grep -A 5 <span class="hljs-string">"group_name"</span>
</code></pre>
<ol start="2">
<li><p><strong>Effective Permissions Check</strong>:</p>
<ul>
<li>Combine the script output with the OCI CLI's policy parser to validate effective permissions:</li>
</ul>
</li>
</ol>
<pre><code class="lang-bash">    oci iam policy get --policy-id &lt;policy_id&gt; --query <span class="hljs-string">"data.statements[]"</span>
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This script provides a foundation for maintaining good security hygiene in your OCI environment. Regular auditing of IAM policies helps ensure proper access controls and compliance with security requirements. While the OCI Console offers many powerful features, using CLI tools like this script can significantly improve visibility into your IAM policy structure and help maintain a secure cloud environment.</p>
<p>Remember to run this script in a controlled manner and during off-peak hours if you have a large number of compartments, as it makes multiple API calls to the OCI service.</p>
]]></content:encoded></item><item><title><![CDATA[Oracle Data Pump and OCI]]></title><description><![CDATA[Oracle Data Pump is a powerful utility for moving data and metadata between Oracle databases. When combined with Oracle Cloud Infrastructure (OCI), it becomes an even more versatile tool for database administrators and developers. This guide will wal...]]></description><link>https://devkata.joergeous.one/oracle-data-pump-and-oci</link><guid isPermaLink="true">https://devkata.joergeous.one/oracle-data-pump-and-oci</guid><category><![CDATA[Oracle Autonomous Database]]></category><category><![CDATA[datapump]]></category><dc:creator><![CDATA[Joe Ngo]]></dc:creator><pubDate>Sun, 08 Sep 2024 15:58:14 GMT</pubDate><content:encoded><![CDATA[<p>Oracle Data Pump is a powerful utility for moving data and metadata between Oracle databases. When combined with Oracle Cloud Infrastructure (OCI), it becomes an even more versatile tool for database administrators and developers. This guide will walk you through the process of setting up and using Data Pump with OCI, covering everything from credential creation to monitoring job status.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#setting-up-oci-credentials-in-autonomous-database">Setting Up OCI Credentials in Autonomous Database</a></p>
</li>
<li><p><a class="post-section-overview" href="#installing-data-pump-on-oci-oracle-linux">Installing Data Pump on OCI Oracle Linux</a></p>
</li>
<li><p><a class="post-section-overview" href="#configuring-the-oracle-wallet">Configuring the Oracle Wallet</a></p>
</li>
<li><p><a class="post-section-overview" href="#executing-data-pump-export">Executing Data Pump Export</a></p>
</li>
<li><p><a class="post-section-overview" href="#working-with-object-storage">Working with Object Storage</a></p>
</li>
<li><p><a class="post-section-overview" href="#executing-data-pump-import">Executing Data Pump Import</a></p>
</li>
<li><p><a class="post-section-overview" href="#monitoring-data-pump-execution-status">Monitoring Data Pump Execution Status</a></p>
</li>
<li><p><a class="post-section-overview" href="#archiving-files-on-object-storage">Archiving Files on Object Storage</a></p>
</li>
</ol>
<h2 id="heading-setting-up-oci-credentials-in-autonomous-database">Setting Up OCI Credentials in Autonomous Database</h2>
<p>Before you can use Data Pump with OCI Object Storage, you need to set up the appropriate credentials in your Autonomous Database. Here's how to do it:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">BEGIN</span>
  DBMS_CLOUD.CREATE_CREDENTIAL(
    credential_name =&gt; <span class="hljs-string">'&lt;credential&gt;'</span>,
    username =&gt; <span class="hljs-string">'&lt;oci-username&gt;'</span>,
    <span class="hljs-keyword">password</span> =&gt; <span class="hljs-string">'&lt;auth-token&gt;'</span>
  );
<span class="hljs-keyword">END</span>;
/
</code></pre>
<p><strong>NOTE:</strong> Currently, Data Pump CLI commands using OCI Object Storage only works with auth-token credentials. Credentials created using API Keys would fail to authenticate.</p>
<p>If you're running Data Pump operations through another user, make sure to grant the necessary permissions:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">GRANT</span> datapump_cloud_exp <span class="hljs-keyword">TO</span> &lt;schema_user&gt;;
<span class="hljs-keyword">GRANT</span> datapump_cloud_imp <span class="hljs-keyword">TO</span> &lt;schema_user&gt;;
<span class="hljs-keyword">GRANT</span> <span class="hljs-keyword">READ</span>, WRITE <span class="hljs-keyword">ON</span> <span class="hljs-keyword">DIRECTORY</span> data_pump_dir <span class="hljs-keyword">TO</span> &lt;schema_user&gt;;
</code></pre>
<h2 id="heading-installing-data-pump-on-oci-oracle-linux">Installing Data Pump on OCI Oracle Linux</h2>
<p>To use Data Pump on an OCI Oracle Linux instance, you'll need to install the Oracle Instant Client. The installation process differs slightly depending on your Oracle Linux version.</p>
<p>For Oracle Linux 7:</p>
<pre><code class="lang-sh">sudo yum install oracle-instantclient-release-el7
sudo yum install oracle-instantclient-basic
sudo yum install oracle-instantclient-tools
</code></pre>
<p>For Oracle Linux 8:</p>
<pre><code class="lang-sh">sudo dnf install oracle-instantclient-release-el8
sudo dnf install oracle-instantclient-basic
sudo dnf install oracle-instantclient-tools
</code></pre>
<h2 id="heading-configuring-the-oracle-wallet">Configuring the Oracle Wallet</h2>
<p>To connect to your Autonomous Database, you'll need to set up the Oracle Wallet:</p>
<ol>
<li><p>Unzip the wallet contents to a folder.</p>
</li>
<li><p>Edit the <code>sqlnet.ora</code> file. Replace <code>?/network/admin</code> with the location of the wallet contents.</p>
</li>
</ol>
<h2 id="heading-executing-data-pump-export">Executing Data Pump Export</h2>
<p>Now that everything is set up, you can perform a Data Pump export. Here's an example command that exports directly to OCI Object Storage:</p>
<pre><code class="lang-sh"><span class="hljs-built_in">export</span> PATH=/usr/lib/oracle/21/client64/bin:<span class="hljs-variable">$PATH</span>
<span class="hljs-built_in">export</span> TNS_ADMIN=&lt;location of exploded wallet&gt;

expdp &lt;db-username&gt;@&lt;adb-service-name&gt; filesize=5GB credential=&lt;credential&gt; dumpfile=https://objectstorage.&lt;region&gt;.oraclecloud.com/n/&lt;namespace&gt;/b/&lt;bucket&gt;/o/&lt;file-prefix&gt;%U.dmp logfile=&lt;logname&gt;.<span class="hljs-built_in">log</span> directory=data_pump_dir
</code></pre>
<p>If you prefer to export to the database first and then transfer to Object Storage, use this command:</p>
<pre><code class="lang-sh">expdp &lt;db-username&gt;@&lt;adb-service-name&gt; filesize=5GB dumpfile=&lt;file-prefix&gt;%U.dmp logfile=&lt;logname&gt;.<span class="hljs-built_in">log</span> directory=data_pump_dir
</code></pre>
<p>Then, transfer the file using <code>DBMS_CLOUD.PUT_OBJECT</code>.</p>
<h2 id="heading-working-with-object-storage">Working with Object Storage</h2>
<h3 id="heading-downloading-files-from-object-storage">Downloading Files from Object Storage</h3>
<p>To download files from Object Storage:</p>
<pre><code class="lang-sh"><span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> $(seq -f <span class="hljs-string">"%02g"</span> 1 &lt;n&gt;); <span class="hljs-keyword">do</span> 
  curl -O -X GET -u <span class="hljs-string">'&lt;oci-username&gt;:&lt;auth-token&gt;'</span> https://swiftobjectstorage.&lt;region&gt;.oraclecloud.com/v1/&lt;namespace&gt;/&lt;bucket&gt;/&lt;file-prefix&gt;<span class="hljs-variable">${i}</span>.dmp
<span class="hljs-keyword">done</span>
</code></pre>
<p>Replace <code>&lt;n&gt;</code> with the number of dump files.</p>
<h3 id="heading-uploading-files-to-object-storage">Uploading Files to Object Storage</h3>
<p>To upload files to Object Storage:</p>
<pre><code class="lang-sh"><span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> $(seq -f <span class="hljs-string">"%02g"</span> 1 &lt;n&gt;); <span class="hljs-keyword">do</span> 
  curl -X PUT -u <span class="hljs-string">'&lt;oci-username&gt;:&lt;auth-token&gt;'</span> -T &lt;file-prefix&gt;<span class="hljs-variable">${i}</span>.dmp https://swiftobjectstorage.&lt;region&gt;.oraclecloud.com/v1/&lt;namespace&gt;/&lt;bucket&gt;/&lt;file-prefix&gt;<span class="hljs-variable">${i}</span>.dmp
<span class="hljs-keyword">done</span>
</code></pre>
<h3 id="heading-copying-files-from-adb-to-object-storage">Copying Files from ADB to Object Storage</h3>
<p>You can copy files from your Autonomous Database to Object Storage using PL/SQL:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">BEGIN</span>
  DBMS_CLOUD.PUT_OBJECT(
    credential_name =&gt; <span class="hljs-string">'&lt;credential&gt;'</span>,
    object_uri =&gt; <span class="hljs-string">'https://objectstorage.&lt;region&gt;.oraclecloud.com/n/&lt;namespace&gt;/b/&lt;bucket&gt;/o/&lt;filename&gt;'</span>,
    directory_name  =&gt; <span class="hljs-string">'DATA_PUMP_DIR'</span>,
    file_name =&gt; <span class="hljs-string">'&lt;filename&gt;'</span>);
<span class="hljs-keyword">END</span>;
/
</code></pre>
<h3 id="heading-listing-objects-in-an-object-storage-bucket">Listing Objects in an Object Storage Bucket</h3>
<p>To list objects in your bucket:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> *
<span class="hljs-keyword">FROM</span> dbms_cloud.list_objects(
  credential_name =&gt; <span class="hljs-string">'&lt;credential&gt;'</span>, 
  location_uri =&gt; <span class="hljs-string">'https://objectstorage.&lt;region&gt;.oraclecloud.com/n/&lt;namespace&gt;/b/&lt;bucket&gt;/o/'</span>
);
</code></pre>
<h2 id="heading-executing-data-pump-import">Executing Data Pump Import</h2>
<p>To import data from Object Storage:</p>
<pre><code class="lang-sh"><span class="hljs-built_in">export</span> PATH=/usr/lib/oracle/21/client64/bin:<span class="hljs-variable">$PATH</span>
<span class="hljs-built_in">export</span> TNS_ADMIN=&lt;location of exploded wallet&gt;

impdp &lt;db-username&gt;@&lt;adb-service-name&gt; credential=&lt;credential&gt; dumpfile=https://objectstorage.&lt;region&gt;.oraclecloud.com/n/&lt;namespace&gt;/b/&lt;bucket&gt;/o/&lt;file-prefix&gt;%U.dmp logfile=&lt;logname&gt;.<span class="hljs-built_in">log</span> directory=data_pump_dir
</code></pre>
<h2 id="heading-monitoring-data-pump-execution-status">Monitoring Data Pump Execution Status</h2>
<p>You can monitor the status of your Data Pump jobs using this SQL query:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span>
    opname,
    <span class="hljs-keyword">sid</span>,
    <span class="hljs-built_in">serial</span><span class="hljs-comment">#,</span>
    <span class="hljs-keyword">context</span>,
    sofar,
    totalwork,
    <span class="hljs-keyword">round</span>(sofar / totalwork * <span class="hljs-number">100</span>, <span class="hljs-number">2</span>) <span class="hljs-string">"%_COMPLETE"</span>
<span class="hljs-keyword">FROM</span>
    v$session_longops
<span class="hljs-keyword">WHERE</span>
    opname <span class="hljs-keyword">IN</span> (
        <span class="hljs-keyword">SELECT</span>
            d.job_name
        <span class="hljs-keyword">FROM</span>
            v$<span class="hljs-keyword">session</span>             s,
            v$process             p,
            dba_datapump_sessions d
        <span class="hljs-keyword">WHERE</span>
            p.addr = s.paddr
            <span class="hljs-keyword">AND</span> s.saddr = d.saddr
    )
    <span class="hljs-keyword">AND</span> opname <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">LIKE</span> <span class="hljs-string">'%aggregate%'</span>
    <span class="hljs-keyword">AND</span> totalwork != <span class="hljs-number">0</span>
    <span class="hljs-keyword">AND</span> sofar &lt;&gt; totalwork;
</code></pre>
<h2 id="heading-archiving-files-on-object-storage">Archiving Files on Object Storage</h2>
<p>After your Data Pump operation is complete, you might want to archive the dump files to save on storage costs. Here's how to do it:</p>
<pre><code class="lang-sh"><span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> $(seq -f <span class="hljs-string">"%02g"</span> 1 &lt;n&gt;); <span class="hljs-keyword">do</span> 
  oci os object update-storage-tier -bn dpexp --object-name &lt;file-prefix&gt;<span class="hljs-variable">${i}</span>.dmp  --storage-tier Archive
  oci os object rename  -bn dpexp --source-name &lt;file-prefix&gt;<span class="hljs-variable">${i}</span>.dmp --new-name <span class="hljs-string">"archive/&lt;file-prefix&gt;<span class="hljs-variable">${i}</span>.dmp"</span>
<span class="hljs-keyword">done</span>
</code></pre>
<p>This script changes the storage tier to Archive and moves the files to an "archive" folder in your bucket.</p>
<p>By following this guide, you should now have a solid understanding of how to use Oracle Data Pump with OCI, from setting up credentials to archiving your dump files. Remember to always test these operations in a non-production environment before applying them to your production databases.</p>
]]></content:encoded></item><item><title><![CDATA[Automating APEX Service Account Unlocking in Oracle]]></title><description><![CDATA[In the world of Oracle Application Express (APEX) development, maintaining smooth operations is crucial. One common challenge faced by APEX administrators is dealing with locked service accounts. Today, we'll explore an elegant solution that automate...]]></description><link>https://devkata.joergeous.one/automating-apex-service-account-unlocking-in-oracle</link><guid isPermaLink="true">https://devkata.joergeous.one/automating-apex-service-account-unlocking-in-oracle</guid><category><![CDATA[Oracle Database]]></category><dc:creator><![CDATA[Joe Ngo]]></dc:creator><pubDate>Fri, 06 Sep 2024 07:42:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/G12aLQLBLuA/upload/0721268e157cbb5784d7fdacd90d6727.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the world of Oracle Application Express (APEX) development, maintaining smooth operations is crucial. One common challenge faced by APEX administrators is dealing with locked service accounts. Today, we'll explore an elegant solution that automates the process of unlocking these accounts, ensuring uninterrupted service and reducing manual intervention. Importantly, we'll also discuss why this solution avoids creating objects directly under the SYS schema, adhering to best practices for Oracle database security.</p>
<h2 id="heading-the-problem">The Problem</h2>
<p>APEX service accounts can become locked due to various reasons, such as failed login attempts or security policies. When this happens, it can disrupt operations and require immediate attention from database administrators. Manually unlocking these accounts can be time-consuming and prone to oversight, especially in large-scale environments.</p>
<h2 id="heading-the-solution">The Solution</h2>
<p>We've developed a PL/SQL procedure that automatically identifies and unlocks APEX service accounts. This solution is then scheduled to run at regular intervals, providing a hands-off approach to maintaining account accessibility. Crucially, our approach creates a dedicated schema for this operation, avoiding the common pitfall of creating objects under the SYS schema.</p>
<p>Let's break down the solution step by step:</p>
<h3 id="heading-1-creating-a-dedicated-user">1. Creating a Dedicated User</h3>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">USER</span> apex_ops_admin <span class="hljs-keyword">NO</span> <span class="hljs-keyword">AUTHENTICATION</span>;
<span class="hljs-keyword">GRANT</span> <span class="hljs-keyword">READ</span> <span class="hljs-keyword">ON</span> dba_users <span class="hljs-keyword">TO</span> apex_ops_admin;
</code></pre>
<p>We start by creating a dedicated user <code>apex_ops_admin</code> with no authentication. This user is granted read access to the <code>dba_users</code> view, allowing it to query user account information.</p>
<p><strong>Important Note</strong>: Creating a separate user for this operation, rather than using the SYS schema, is a critical security best practice. The SYS schema is reserved for core database objects and should not be used for custom applications or procedures. By using a dedicated schema:</p>
<ol>
<li><p>We maintain a clear separation of concerns.</p>
</li>
<li><p>We reduce the risk of accidentally modifying or dropping critical system objects.</p>
</li>
<li><p>We can more easily manage and audit the privileges granted for this specific task.</p>
</li>
</ol>
<h3 id="heading-2-the-unlocking-procedure">2. The Unlocking Procedure</h3>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">OR</span> <span class="hljs-keyword">REPLACE</span> <span class="hljs-keyword">PROCEDURE</span> apex_ops_admin.unlock_service_accounts
    <span class="hljs-keyword">AUTHID</span> <span class="hljs-keyword">current_user</span>
<span class="hljs-keyword">IS</span>
<span class="hljs-keyword">BEGIN</span>
    <span class="hljs-keyword">FOR</span> u <span class="hljs-keyword">IN</span> (
        <span class="hljs-keyword">SELECT</span> username
        <span class="hljs-keyword">FROM</span> dba_users
        <span class="hljs-keyword">WHERE</span> profile = <span class="hljs-string">'APEX_SYSTEM_USER'</span>
          <span class="hljs-keyword">AND</span> account_status = <span class="hljs-string">'LOCKED'</span>
    ) <span class="hljs-keyword">LOOP</span>
        <span class="hljs-keyword">EXECUTE</span> <span class="hljs-keyword">IMMEDIATE</span> <span class="hljs-string">'ALTER USER '</span> || u.username || <span class="hljs-string">' ACCOUNT UNLOCK'</span>;
    <span class="hljs-keyword">END</span> <span class="hljs-keyword">LOOP</span>;
<span class="hljs-keyword">END</span>;
</code></pre>
<p>This procedure does the heavy lifting. It identifies locked APEX service accounts by querying the <code>dba_users</code> view and unlocks them using dynamic SQL. Note that this procedure is created under the <code>apex_ops_admin</code> schema, not SYS.</p>
<h3 id="heading-3-granting-necessary-privileges">3. Granting Necessary Privileges</h3>
<pre><code class="lang-sql"><span class="hljs-keyword">GRANT</span> INHERIT <span class="hljs-keyword">PRIVILEGES</span> <span class="hljs-keyword">ON</span> <span class="hljs-keyword">USER</span> <span class="hljs-keyword">sys</span> <span class="hljs-keyword">TO</span> apex_ops_admin;
</code></pre>
<p>This grant allows <code>apex_ops_admin</code> to inherit privileges from SYS, ensuring it has the necessary permissions to unlock accounts when executed by SYS. This approach is more secure than creating the procedure directly under SYS, as it allows for more granular control over the privileges.</p>
<h3 id="heading-4-scheduling-the-job">4. Scheduling the Job</h3>
<pre><code class="lang-sql"><span class="hljs-keyword">BEGIN</span>
    dbms_scheduler.create_job(
        job_name =&gt; <span class="hljs-string">'APEX$_UNLOCK_SERVICE_ACCOUNTS'</span>,
        job_type =&gt; <span class="hljs-string">'PLSQL_BLOCK'</span>,
        job_action =&gt; <span class="hljs-string">'BEGIN apex_ops_admin.unlock_service_accounts; END;'</span>,
        start_date =&gt; systimestamp,
        repeat_interval =&gt; <span class="hljs-string">'FREQ=MINUTELY;INTERVAL=15'</span>,
        enabled =&gt; <span class="hljs-literal">TRUE</span>
    );
<span class="hljs-keyword">END</span>;
</code></pre>
<p>Finally, we create a scheduler job under SYS that runs our procedure every 15 minutes. This ensures that locked accounts are promptly unlocked without manual intervention.</p>
<h2 id="heading-benefits-and-considerations">Benefits and Considerations</h2>
<p>This solution offers several advantages:</p>
<ol>
<li><p><strong>Automation</strong>: Reduces the need for manual checks and interventions.</p>
</li>
<li><p><strong>Timeliness</strong>: Ensures that locked accounts are quickly unlocked, minimizing downtime.</p>
</li>
<li><p><strong>Scalability</strong>: Works efficiently regardless of the number of APEX service accounts.</p>
</li>
<li><p><strong>Security</strong>: By avoiding the creation of objects under the SYS schema, we adhere to Oracle security best practices.</p>
</li>
</ol>
<p>However, it's important to note that while this solution addresses the symptom (locked accounts), it doesn't address the root cause of why accounts are being locked. It's crucial to monitor and investigate the reasons behind frequent account lockouts to enhance overall system security and stability.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>By implementing this automated solution, APEX administrators can significantly reduce the operational overhead associated with managing service account lockouts. This not only improves system reliability but also frees up valuable time for more strategic tasks.</p>
<p>Remember, while automation is powerful, it should be implemented thoughtfully. Always ensure that your automation scripts align with your organization's security policies and best practices. In this case, by creating a dedicated schema for our automation task, we've demonstrated how to balance efficiency with security, avoiding the pitfalls of working directly within the SYS schema.</p>
]]></content:encoded></item><item><title><![CDATA[Installing Mermaid CLI on Ubuntu VM]]></title><description><![CDATA[Make sure to pick x86-64 VM as Puppeteer, which mermaid-cli is dependent on, is not available for ARM processors. (Reference)
Dependencies
To successfully run Mermaid CLI, you need to install several dependencies that Puppeteer relies on. Execute the...]]></description><link>https://devkata.joergeous.one/installing-mermaid-cli-on-ubuntu-vm</link><guid isPermaLink="true">https://devkata.joergeous.one/installing-mermaid-cli-on-ubuntu-vm</guid><category><![CDATA[MermaidJS]]></category><dc:creator><![CDATA[Joe Ngo]]></dc:creator><pubDate>Wed, 12 Jun 2024 10:38:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/op5zrC8iaDQ/upload/34917c6d1ae9f94ff6a901cee327bc5c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Make sure to pick x86-64 VM as Puppeteer, which mermaid-cli is dependent on, is not available for ARM processors. (<a target="_blank" href="https://pptr.dev/troubleshooting">Reference</a>)</p>
<h2 id="heading-dependencies">Dependencies</h2>
<p>To successfully run Mermaid CLI, you need to install several dependencies that Puppeteer relies on. Execute the following command to install all the necessary packages:</p>
<pre><code class="lang-bash">sudo apt install ca-certificates fonts-liberation libasound2 libatk-bridge2.0-0 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm1 libgcc1 libglib2.0-0 libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 lsb-release wget xdg-utils
</code></pre>
<p>These libraries ensure that Puppeteer can function correctly on your Ubuntu VM, providing the necessary graphical and system support.</p>
<h2 id="heading-install-nodejs-and-npm">Install Node.js and npm</h2>
<p>To manage Node.js versions efficiently, it's recommended to use the Node Version Manager (NVM). This tool allows you to install multiple versions of Node.js and switch between them effortlessly.</p>
<p>As the application user, follow the instructions to install NVM from its <a target="_blank" href="https://github.com/nvm-sh/nvm?tab=readme-ov-file#installing-and-updating">GitHub repository</a>. You can typically install NVM by running:</p>
<pre><code class="lang-bash">wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
</code></pre>
<p>Once NVM is installed, load it into your shell session:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> NVM_DIR=<span class="hljs-string">"<span class="hljs-subst">$([ -z <span class="hljs-string">"<span class="hljs-variable">${XDG_CONFIG_HOME-}</span>"</span> ] &amp;&amp; printf %s <span class="hljs-string">"<span class="hljs-variable">${HOME}</span>/.nvm"</span> || printf %s <span class="hljs-string">"<span class="hljs-variable">${XDG_CONFIG_HOME}</span>/nvm"</span>)</span>"</span>
[ -s <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/nvm.sh"</span> ] &amp;&amp; \. <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/nvm.sh"</span>
</code></pre>
<p>Use NVM to install the latest version of Node.js:</p>
<pre><code class="lang-bash">nvm install node
</code></pre>
<h2 id="heading-install-mermaid-cli">Install Mermaid CLI</h2>
<p>After setting up Node.js, you can proceed to install Mermaid CLI globally using npm. This tool allows you to convert Mermaid diagrams into images and other formats directly from the command line.</p>
<p>Run the following command to install Mermaid CLI:</p>
<pre><code class="lang-bash">npm install -g @mermaid-js/mermaid-cli
</code></pre>
<p>By following these steps, you will have a fully functional setup of Mermaid CLI on your Ubuntu VM, ready to generate diagrams and visualizations as needed.</p>
]]></content:encoded></item><item><title><![CDATA[JVM Internals with JCMD: Inspecting Tomcat and ORDS Processes]]></title><description><![CDATA[Properly configured Java Virtual Machine (JVM) settings are crucial for the performance and stability of Java web applications like Apache Tomcat and Oracle REST Data Services (ORDS). Here's a quick overview on leveraging the JCMD utility to troubles...]]></description><link>https://devkata.joergeous.one/jvm-internals-with-jcmd</link><guid isPermaLink="true">https://devkata.joergeous.one/jvm-internals-with-jcmd</guid><category><![CDATA[jvm]]></category><dc:creator><![CDATA[Joe Ngo]]></dc:creator><pubDate>Tue, 12 Mar 2024 08:12:25 GMT</pubDate><content:encoded><![CDATA[<p>Properly configured Java Virtual Machine (JVM) settings are crucial for the performance and stability of Java web applications like Apache Tomcat and Oracle REST Data Services (ORDS). Here's a quick overview on leveraging the JCMD utility to troubleshoot and resolve JVM configuration problems in these platforms. By exploring various JCMD commands, you'll gain insights into memory usage, garbage collection, and other JVM metrics, enabling you to identify and fix configuration issues proactively.</p>
<ol>
<li><p><strong>Locate the Process ID (PID)</strong>: First, you need to find out the process ID of your Tomcat or ORDS instance. You can do this by using tools like ps (on Unix-based systems) or Task Manager (on Windows) or any other process monitoring tool you prefer.</p>
</li>
<li><p><strong>Use jcmd command</strong>: Once you have the PID, you can use jcmd to view JVM details. The general syntax is:</p>
</li>
</ol>
<pre><code class="lang-plaintext">jcmd ${PID} {command}
</code></pre>
<p>For example, to view the VM flags, you would use:</p>
<pre><code class="lang-plaintext">jcmd ${PID} VM.flags
</code></pre>
<p>Or to view the VM system properties:</p>
<pre><code class="lang-plaintext">jcmd ${PID} VM.system_properties
</code></pre>
<p>Replace <code>${PID}</code> with the process ID of your Tomcat instance and <code>{command}</code> with the specific command you want to execute.</p>
<hr />
<p>There are several useful jcmd commands for diagnosing and monitoring a Java process such as Tomcat. Here are some commonly used ones:</p>
<ul>
<li><strong>VM.version</strong>: Displays the version information of the JVM.</li>
</ul>
<pre><code class="lang-plaintext">jcmd ${PID} VM.version
</code></pre>
<ul>
<li><strong>VM.flags</strong>: Displays the VM flags and their values.</li>
</ul>
<pre><code class="lang-plaintext">jcmd ${PID} VM.flags
</code></pre>
<ul>
<li><strong>VM.system_properties</strong>: Lists all system properties.</li>
</ul>
<pre><code class="lang-plaintext">jcmd ${PID} VM.system_properties
</code></pre>
<ul>
<li><strong>VM.command_line</strong>: Shows the command-line options passed to the JVM.</li>
</ul>
<pre><code class="lang-plaintext">jcmd ${PID} VM.command_line
</code></pre>
<ul>
<li><strong>GC.heap_info</strong>: Provides information about heap memory usage.</li>
</ul>
<pre><code class="lang-plaintext">jcmd ${PID} GC.heap_info
</code></pre>
<ul>
<li><strong>GC.class_histogram</strong>: Prints a histogram of the number of instances of each class.</li>
</ul>
<pre><code class="lang-plaintext">jcmd ${PID} GC.class_histogram
</code></pre>
<ul>
<li><a target="_blank" href="http://GC.run"><strong>GC.run</strong></a>: Initiates garbage collection.</li>
</ul>
<pre><code class="lang-plaintext">jcmd ${PID} GC.run
</code></pre>
<ul>
<li><strong>Thread.print</strong>: Prints stack traces for all threads.</li>
</ul>
<pre><code class="lang-plaintext">jcmd ${PID} Thread.print
</code></pre>
<ul>
<li><strong>VM.native_memory</strong>: Prints information about native memory usage.</li>
</ul>
<pre><code class="lang-plaintext">jcmd ${PID} VM.native_memory
</code></pre>
<ul>
<li><strong>VM.uptime</strong>: Displays the uptime of the JVM.</li>
</ul>
<pre><code class="lang-plaintext">jcmd ${PID} VM.uptime
</code></pre>
<hr />
]]></content:encoded></item><item><title><![CDATA[3-legged OAuth with Oracle APEX]]></title><description><![CDATA[As digital ecosystems continue to expand, users often seek seamless and secure methods to access third-party services without compromising their credentials. This is where 3-legged OAuth, a robust authentication protocol, emerges as a crucial solutio...]]></description><link>https://devkata.joergeous.one/3-legged-oauth-with-oracle-apex</link><guid isPermaLink="true">https://devkata.joergeous.one/3-legged-oauth-with-oracle-apex</guid><category><![CDATA[#oracle-apex]]></category><category><![CDATA[oauth]]></category><dc:creator><![CDATA[Joe Ngo]]></dc:creator><pubDate>Fri, 08 Mar 2024 17:16:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/6pflEeSzGUo/upload/380afdeb39bceaf603f309e70d400920.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As digital ecosystems continue to expand, users often seek seamless and secure methods to access third-party services without compromising their credentials. This is where 3-legged OAuth, a robust authentication protocol, emerges as a crucial solution. 3-Legged OAuth facilitates the secure delegation of user credentials to third-party applications, allowing them to access resources on behalf of the user. The process involves three distinct entities: the user, the client application, and the server providing the requested resources. By introducing user consent into the authentication workflow, 3-legged OAuth ensures transparency and user-driven authorization.</p>
<p>This <a target="_blank" href="https://medium.com/oracledevs/3-legged-oauth-flow-to-invoke-fusion-apps-rest-endpoints-5fc6b0b7b059">medium article</a> discusses how to implement 3-legged OAuth with Fusion as the resource server.</p>
<p>This post is going to discuss how to implement this with Oracle APEX as the client application, some gotchas and how to get around them.</p>
<p>The main hurdle you’ll encounter with APEX as the client application is receiving the authorization code from the authorization server (IDCS aka IAM). APEX user sessions have their session ID embedded on the request URL every time. If the redirect URL for receiving the authorization code is an APEX application page, then when IDCS redirects to this page with the auth code, APEX will require the user to log in again since the request will be treated as a new session because there is no session ID passed to the URL. Even if you make the redirect page a public page, it will invalidate the existing user session.</p>
<p>So the solution is to have the redirect URL be a RESTful Service URL in APEX. But RESTful Services endpoints are stateless — how do you tie it to the user’s session?</p>
<p>The trick is to add a state parameter to the IDCS authorization redirect:</p>
<pre><code class="lang-plaintext">https://&lt;IDCS HOST&gt;/oauth2/v1/authorize?client_id=&lt;CLIENT ID&gt;&amp;response_type=code&amp;redirect_uri=&lt;REDIRECT URL AS CONFIGURED IN CLIENT&gt;&amp;scope=&lt;SCOPE AS CONFIGURED IN CLIENT ALLOWED SCOPE&gt;&amp;state=&lt;STATE PARAMETER&gt;
</code></pre>
<p>For the state parameter, you can pass the app ID, page ID and the user’s session ID. The session ID can be retrieved using the APEX_CUSTOM_AUTH.GET_SESSION_ID function. Use a delimiter such as a dot (.) to concatenate these 3 values into a single parameter value.</p>
<p>The state parameter is passed along to your redirect URL when authorization server sends back the auth code.</p>
<p>Implement the RESTful Service handler as follows:</p>
<ul>
<li><p>Method: GET</p>
</li>
<li><p>Source Type: PL/SQL</p>
</li>
<li><p>Parameters: auth &amp; state as URI IN parameters</p>
</li>
<li><p>Source:</p>
</li>
</ul>
<pre><code class="lang-sql"><span class="hljs-keyword">DECLARE</span>
    l_state_parts apex_t_varchar2;
    l_app_id      number;
    l_page_id     number;
    l_session_id  number;
<span class="hljs-keyword">BEGIN</span>
    htp.htmlopen;
    htp.headopen;
    htp.title('Fusion Apps Authorization');
    htp.style('
body {
    font-family: Arial, sans-serif;
    text-align: center;
    margin: 50px;
}
.success {
    color: green;
}
.error {
    color: red;
}

');
    htp.headclose;
    htp.bodyopen;

    <span class="hljs-keyword">BEGIN</span>
        l_state_parts := apex_string.split(:state, <span class="hljs-string">'.'</span>);
        l_app_id      := l_state_parts(1);
        l_page_id     := l_state_parts(2);
        l_session_id  := l_state_parts(3);

        apex_session.attach(p_app_id =&gt; l_app_id, p_page_id =&gt; l_page_id, p_session_id =&gt; l_session_id);

        <span class="hljs-comment">/*** Update APEX application item to receive the auth code from :code bind variable ***/</span>

        htp.print('&lt;h1 class="success"&gt;Authorization Successful!&lt;/h1&gt;&lt;p&gt;You may close this page.&lt;/p&gt;');
    EXCEPTION
        WHEN OTHERS THEN
            htp.print('&lt;h1 class="error"&gt;Invalid Request&lt;/h1&gt;&lt;p&gt;An error occured.&lt;/p&gt;');
    <span class="hljs-keyword">END</span>;

    htp.bodyclose;
    htp.htmlclose;
<span class="hljs-keyword">END</span>;
</code></pre>
<p>The APEX_SESSION.ATTACH allows the RESTful service handler to attach to the user’s session so it can manipulate application items or collections within that session.</p>
]]></content:encoded></item></channel></rss>