Roles, definer’s rights, and why your Data Pump procedure suddenly breaks

I ran into this while testing what should have been a pretty simple setup.

A procedure that calls DBMS_DATAPUMP, executed by another user.

Nothing fancy.

And yet it failed with:

ORA-31631: privileges are required

Which didn’t make sense.

The procedure owner had the right roles.
The grants were there.

And this exact setup used to work in 19c.

Same code. Same grants. Different result.

So what changed?


The Setup

Two users:

dpump
Has full Data Pump privileges:

  • DATAPUMP_EXP_FULL_DATABASE
  • DATAPUMP_IMP_FULL_DATABASE

dpump_user
Just enough to run the procedure:

grant connect to dpump_user;
grant execute on expdp_schema to dpump_user;

The procedure is owned by dpump and internally calls DBMS_DATAPUMP.

The idea is straightforward:

Let a low-privilege user run exports without giving them full access.


What I Expected

This is a definer’s rights procedure.

So the mental model is:

“Use the privileges of the procedure owner, not the caller.”

That’s how it behaved in 19c and 21c.


What Actually Happens

On 23ai / 26ai:

ORA-31631: privileges are required

Even though:

  • The owner has the role
  • The role is correctly granted

That’s the part that’s confusing.


What Tripped Me Up

The assumption is that roles “just work” inside PL/SQL.

They don’t. At least not here.

Something changed in newer releases, and you really notice it when you hit this.

When it’s called from inside a stored procedure:

  • Role-based privileges aren’t used the way you expect
  • Having the role is not enough
  • Definer’s rights alone don’t save you here

So even though everything looks correct, it still fails.


First Thing I Tried

Switching to invoker’s rights:

CREATE OR REPLACE PROCEDURE expdp_schema
AUTHID CURRENT_USER
AS
...

This actually does work — just not for this use case.

With AUTHID CURRENT_USER, you’re saying:

“Only run this if the caller already has the required privileges.”

So:

  • dpump works
  • dpump_user fails

Which is expected.

And to be fair, this is a good pattern when you want to enforce that.

If your goal is:

“Only allow users who already have Data Pump privileges to run this”

Then AUTHID CURRENT_USER is exactly what you want.


But That Wasn’t the Goal Here

In this case, the goal was different:

Let a low-privilege user run a very specific Data Pump operation, without giving them full access.

So instead of checking the user’s privileges, the idea is:

Hide the complexity behind a controlled interface.


What Actually Worked

The fix ended up being simpler than expected.

Grant the role directly to the procedure:

grant DATAPUMP_EXP_FULL_DATABASE to PROCEDURE dpump.expdp_schema;

Why This Works

Now the procedure itself has the required privilege.

Which means:

  • The caller doesn’t need Data Pump roles
  • The logic stays controlled inside the procedure
  • The user can only do exactly what the procedure allows

It basically turns the procedure into a black box:
The user can run it.
They just can’t go beyond it.


Takeaway

There are really two valid approaches here, depending on what you want:

Use AUTHID CURRENT_USER when:

  • You want to enforce privilege checks on the caller
  • Users should already have the required roles

Grant the role to the procedure when:

You’re building a “black box” interface

You want to expose a controlled operation

You don’t want to give broad privileges to users

Leave a comment

About THIS SITE

I write about Oracle databases, with a focus on Data Pump, patching, and upgrades.

This site is a collection of things that are easy to get wrong, hard to understand, or not well explained. Most posts come from real issues I have run into while working with Oracle systems.

If you have ever hit a Data Pump error and had no idea what it meant, you are in the right place.