Implementing a Database Authentication Scheme in APEX

The following tangential opening was written especially for Scott Wesley in the hope that he’ll be minded to point out any errors in what follows. The same applies to Jeff Kemp ( although I don’t know if he’s into the AFL).
Unlike me, both of these guys are APEX experts.

Football. It’s a term that means different things to different people.
To a European, it’s most likely to be a reference to good old Association Football ( or Soccer).
To an American, it’s more likely to be the Grid-iron game.
A New Zealander will probably immediately think of Rugby Union.
An Australian ? Well, it’s probably a fair bet that they’ll think of Aussie Rules Football.

On the face of it, the rules appear rather arcane to an outsider. 18-a-side teams kicking, catching and punching something that resembles a Rugby ball around a pitch that resembles a cricket oval. Then there is the scoring system.
“Nice Behind”, to an AFL player is more likely to be taken as a compliment of their skill at the game than an appreciation of their anatomy.

Then again, it’s easy to scoff at any sport with which you are unfamiliar.
For example, Rugby could be characterised as 30 people chasing after an egg. Occasionally, they all stop and half of them go into some strange kind of group hug. I wonder if the backs ever get paranoid because they think the forwards are talking about them ?

As for soccer, even afficionados will acknowledge that there’s something a bit odd about a game where 22 millionares spend lots of time chasing after one ball…when they’re not rolling around in apparent agony after appearing to trip over an earth worm. I mean, the ball isn’t that expensive, surely they can afford one each ?

The point of all of this ? Well, what is considered to be obscure, eccentric, or just plain odd often depends on the perspective of the observer.

Take APEX authentication schemes for example.
Whilst not the default, Database Authentication is a scheme that is readily available. However, there doesn’t seem to be much written on this subject.

In contrast, there is a fair bit out there about APEX Custom Authentication. A lot of it would appear to re-enforce the idea that implementing security by hand is fraught with difficulty.
Just one example can be seen here.

If we were to approach this topic from the perspective of looking to migrate an elderly Oracle Forms application – where each user has their own database account – to APEX, we might be attracted to the idea of a Database Authentication Scheme and want to find out more.

What follows is my adventure through setting up such an Authentication Scheme.
Specifically, I’m going to cover :

  • Creating an APEX Database Authentication Scheme
  • Default behaviour
  • Adding a Verification Function to restrict access to a sub-set of Database Users
  • The vexed question of password resets

Why use Database Authentication

The Oracle documentation states :

“Database Account Credentials is a good choice if having one database account for each named user of your application is feasible and account maintenance using database tools meets your needs.”

If we’re migrating an application from Oracle Forms, then chances are that this is what we’re doing now, so a Database Authentication Scheme should save us a fair bit of work.
The other major advantage is that utilising the Database’s built-in User and Security management means that we don’t have to try and re-invent the wheel.
So, the objective here is to implement Authentication in our new Application without having to :

  • Create and maintain extra tables
  • Write lots of extra code
  • Figure out a secure way of storing passwords

The Application

Firing up my trusty XE 11g installation, I’ll be using a simple APEX application that consist of a standard login page and, initially at least, a Home Page with two read only fields in an HTML Region called WHOAMI.
These are :

  • Application User – the APP_USER that I’m connected to APEX as
  • Database User – the actual user connected to the database

For the P1_APPLICATION_USER, the Source Type is Item (application or page item name).
The source value is APP_USER.
For the P1_DATABASE_USER, the Source Type is set to SQL Query(return single value).
The source value is simply the query :

select user from dual

A Note on the Design

In this example, I’ve taken the approach that the code required to implement this functionality is included in the parsing schema ( HR in this case). As a consequence, the privileges required to execute this code are also granted to the parsing schema.
I’ve done this for the purposes of clarity.
Careful consideration needs to be given to this design decision if you’re planning to implement it in a “proper” production environment.

Creating a Database Authentication Scheme

After navigating to the Application in Application Builder, rather than do anything to the Application itself, we need to create a Shared Component…

db_auth1

The type of component we want is an Authentication Scheme.

NOTE – Authentication Scheme – controls login to the Application.
Authorisation Scheme – governs which bits of the Application the user can see…once they’re connected.

Anyway, in the Security Region, select Authentication Scheme :

db_auth2

…and then hit the Create button…

db_auth3

We want to create a scheme “Based on a pre-configured scheme in the gallery” …

db_auth4

In the next screen :

Name : HR_DB
Scheme Type : Database Accounts

db_auth5

And finally, we click the Create Authentication Scheme button and…

db_auth6

We can see from this that HR_DB is now the Authentication Scheme currently being used by any Application in the Workspace.

Anyway, now to test it.

To this point, I haven’t setup any users for this application.

So, Can I log in as a user that does exist in the database ?
Well, I have a user called MIKE :

select 1
from dba_users
where username = 'MIKE'
/

         1
----------
         1

SQL> 

So, if I now run my application and try to connect using my database credentials…

run_app2

… I can connect using my database credentials.

db_auth_ok1

It’s worth noting that, despite this, the actual database connection from APEX is as the ANONYMOUS user.
If you’re using the APEX Listener instead of the Embedded PL/SQL Gateway (the default in XE), then it’ll probably be APEX_PUBLIC_USER.

So, in order to login to my application, you now have to be a database user.
All the messy password encryption stuff is handled by Oracle and I can now get on with polishing my finely crafted APEX Application….or so you might think.

Just consider this :

run_app_system

…also let’s you connect :

We're not fussy, we'll let anyone in !
We’re not fussy, we’ll let anyone in !

Now, my imaginary Forms application – remember, that’s the one I want to migrate to APEX – may be sitting on a Database Instance with a number of other Applications. So, how do I restrict access to my application to a subset of the users in the database ?
Time for a bit of a re-think then…

The verify function

What we need is a means of identifying a database user as an Application user.
At this point it may well be worth revisiting the role of database roles in APEX applications.
Hang on, you’re thinking, last time you said they were pretty much useless in APEX.
Well, bear with me.

Roles as Privileges, sort of

What we’re going to do here is to simply create an empty role and assign it to a database user :

create role hr_user
/

grant hr_user to mike
/

We now have some means of determining which database users are our application users :

select 1
from dba_role_privs
where granted_role = 'HR_USER'
and grantee = 'MIKE'
/

The function

Now all we need is a function that checks to see if the user attempting to login has this role granted to them.
It’s worth bearing in mind here that, for a function based on the above statement, select privileges on DBA_ROLE_PRIVS is required.

To start with I’m going to grant the privilege to HR :

grant select on sys.dba_role_privs to hr
/

and then I’m going to create the function in the HR schema :

create or replace function is_hr_user_fn
    return boolean
is
--
-- Is this user a database user with privileges to access the APEX Application ?
-- NOTE - the owner of this function requires SELECT privilege on DBA_ROLE_PRIVS
--
    l_dummy pls_integer;
begin
    select 1 
    into l_dummy 
    from sys.dba_role_privs
    where granted_role = 'HR_USER'
    and grantee = apex_040200.v('APP_USER');
    
    return true;
exception
    when no_data_found then
        raise_application_error('-20000', 'You are not an application user');
end;
/ 

You’ll note that the references to both DBA_ROLE_PRIVS and the V function are done directly on the objects themselves rather than through their public synonyms.
In many cases, but especially where security is concerned, it’s usually a good idea to make sure that you’re referencing the object that you intend rather than relying on a synonym.

If you want to see an example of how public synonyms can be changed to point to objects other than those originally intended, then have a look here.

Now we need to tell our Authentication scheme to use this function as the Verify Function.
In the Application Builder, go back to the Shared Components screen then select Authentication Schemes.

Now click on the pencil icon next to HR_DB – Current :

If you want to be a bit more discerning...
If you want to be a bit more discerning…

In the Session Not Valid section, there is a field called Verify Function Name.
In here, simply enter the name of our function – i.e. is_hr_user_fn :

...add a Verify Function
…add a Verify Function

And save the changes.

So, we should now be able to connect as MIKE, but not any other database user.

Connecting as MIKE works as before. However, for SYSTEM, the results are slighty different :

Your name's not down, you're not coming in !
Your name’s not down, you’re not coming in !

As we can see, the Application Error raised by the function is displayed. If you hit the OK button, you’ll then be returned to the Login Page.

The Principle of Least Privilege

In case your not familiar with the term, it basically boils down to the principle that access to an application should be restricted to the minimum level required for a user, application or program to function.
Have a look here for a proper explanation.

It’s probably worth noting that, implementing this approach to Authentication means that, in order to create a new application user, all that’s required is the following :

create user plugger identified by pwd
/

grant hr_user to plugger
/

In case you’re wondering, Plugger is the nickname of a certain Tony Lockett who, apparently, was a pretty good Aussie Rules player in his time.

Anyway, as you can see, our new user requires no system privileges, not even CREATE SESSION. They simply need to be granted the role so that they can be identified as an application user.

Whilst were on the subject of least privilege, you might consider that it is by no means necessary for the parsing schema of an APEX application to have CREATE SESSION priviliges, or indeed, to even be the owner of the application’s database objects.
This applies irrespective of the Authentication Scheme being used.

We now have a robust and efficient Authentication Scheme. There is however, one rather thorny issue that we still need to consider.

Changing Passwords

Whilst we now have a mechanism for authenticating users through their database accounts, unless we give them the facility to change their passwords before they expire, we’ll be storing up a significant amount of admin for the poor, hard-pressed DBA.

The venerable Forms Application we’re migrating was written in the days prior to SSO becoming prevalent and authentication is still managed entirely within the database. Remember, the whole point of chosing Database Authentication is so that we minimise the amount of effort required to migrate this application onto APEX in terms of re-coding the Application’s Security Model.

This is where things get a bit tricky.
Whilst our users are authenticating as themselves, they are actually connecting to the database as ANONYMOUS or APEX_PUBLIC_USER.

Therefore, we need a procedure in a schema with ALTER USER privileges to change passwords from within the APEX application.

So, how do we provide this functionality in our application.

Danger ! Assumption Imminent !

As I’m all too aware ( often through bitter experience), Assumption is the Mother of all cock-ups.
Therefore, the assumption I’m about to make here requires careful explanation.

Here goes then…

I’m assuming that I can safely call a stored procedure from within APEX, passing a user password in clear text.

Clear text ! I hear you cry, Have you gone mad ?
Well, possibly. On the other hand a trawl through of the APEX documentation reveals that there are a few package members in the APEX packages themselves where this takes place.

These are :

Further research reveals that, certainly in the latest versions of APEX, there do not appear to be any exploits available to compromise these procedures. The most recent one I found was for APEX 3.1, an example of which can be seen on the Red Database Security site.

As well as giving the user the ability to change their password at any time, we also want to check immeadiately after the user connects and find out whether their password is near to expiry. If so, then we need to re-direct them to a password change page.
What was Jeff saying about scary code ?

Anyway, the steps to build this functionality are, in order :

  1. Create a Change Password Procedure to be called from the application
  2. Create a Change Password Page where the user can change their password ( and which will call the procedure)
  3. Create a branch in the Application to re-direct a user to the Change Password Page if their password is due to expire

UPDATE :

Now in my original post, I had a whole section in here about not allowing quotes in passwords so that our dynamically built password change command would not be susceptible to injection.
Fortunately, Jeff Kemp took the time to point out the error of my ways (see the comments at the end of this post).
I’ve also now posted a follow up which explores this particular aspect of password changes in more detail.

Allowable characters in the password

As per the update above, I’ve now removed this section completely because it’s not necessary.
The password input parameters to the Change Password Procedure that we come up with should simply be enclosed in double-quotes when being concatenated into the ALTER USER statement.

The Change Password Procedure

Once again, this procedure is being created in the HR schema. It will be used to ultimately issue the ALTER USER command to change the passwords. Therefore, we need to grant the ALTER USER privilege to HR :

grant alter user to hr
/

As this procedure also needs to reference DBA_USERS, we’ll need to grant SELECT on that too.

grant select on sys.dba_users to hr
/

When writing this procedure, paranoia is the watchword. Objects need to be referenced directly, rather than via synonyms and any user input needs to be sanitised before we plug it into the dynamic SQL statement we need to run.
The result might look something like this :

create or replace procedure change_apex_user_pwd_pr
(
    i_old_pwd in varchar2,
    i_new_pwd in varchar2
)
is
--
-- Procedure to change the password for a user of the NEW_HR APEX application
-- The old password is required, as well as the new one because, if we're
-- using a verify function in the profile the user is assigned to, the
-- old password must be specified in the ALTER USER statement.
--
    l_user sys.dba_users.username%type;
    
    lc_apex_user constant sys.dba_users.username%type := 'ANONYMOUS';
    
    l_dummy pls_integer;
    cursor c_validate_user( cp_user sys.dba_users.username%type)
    is
        select 1
        from sys.dba_users usr
        inner join sys.dba_role_privs rol
            on rol.grantee = usr.username
        where usr.username = cp_user;
begin
    --
    -- Make sure that the parameter values have been specified
    --
    if i_new_pwd is null or i_old_pwd is null then
        raise_application_error(-20000, 'Both the Old Password and the New Password must be specified');
    end if;
    --
    -- Additionally, check that the password does not exceed the maximum length
    -- allowed ( 50 in 11g)
    --
    if length( i_old_pwd) > 50 
        or length( i_new_pwd) > 50
    then
        raise_application_error(-20001, 'Passwords must not exceed 50 characters in length.');
    end if;
    --
    -- Now validate that the user is indeed 
    --  (a) calling the function from APEX
    --  (b) exists in the database
    --  (c) is a user of this application

    l_user := apex_040200.v('APP_USER');
    if l_user is null 
        or user != lc_apex_user
    then
        raise_application_error(-20002, 'This function can only be called from APEX');
    end if;
    open c_validate_user( l_user);
    fetch c_validate_user into l_dummy;
    if c_validate_user%notfound then
        close c_validate_user;
        raise_application_error(-20003, 'This user is not a NEW_HR Application user');
    end if;
    close c_validate_user;
    --
    -- Now change the password. REPLACE clause is required in case the
    -- user's default profile has a password verify function specified
    -- Enclose the old and new passwords in double quotes...
    --
    execute immediate 'alter user '||l_user||' identified by '||chr(34)
        ||i_new_pwd||chr(34)||' replace '||chr(34)||i_old_pwd||chr(34);
end;
/

In the procedure itself, we’re taking a number of precautions :

  • Values for both parameters must be supplied
  • The input parameter values must not exceed 50 characters – the maximum length of an 11g password
  • The user currently connected to the database is the Apex user ( in my case ANONYMOUS)
  • A call to the V function for the application user returns a value
  • The application user we’re changing is indeed a valid user of the NEW_HR Apex application – and a database user
  • references to any database objects are done directly and not via synonyms

Hopefully, that’s enough paranoia to prevent the procedure being misused.
We can use a test harness to check the parameter tests at least :

set serveroutput on size unlimited
declare
--
-- test for the change_apex_user_pwd_pr procedure.
-- Note all of these tests should fail as we're running from SQL*Plus and
-- are not connected as ANONYMOUS.
-- 
    type rec_params is record
    (
        old_pwd varchar2(100),
        new_pwd varchar2(100)
    );
    type typ_params is table of rec_params index by pls_integer;
    tbl_params typ_params;
begin
    -- populate the test parameter array
    
    -- Test 1 - missing old password value
    tbl_params(1).old_pwd := null;
    tbl_params(1).new_pwd := 'Boring';
    
    -- Test 2 - missing new password value
    tbl_params(2).old_pwd := 'Boring';
    tbl_params(2).new_pwd := null;
 
    -- Test 3 - old password > 50 characters
    tbl_params(3).old_pwd := 'just leaning on the keyboard until i have printed over 50 characters zzzzz';
    tbl_params(3).new_pwd := 'short_and_to_the_point';
    
    -- Test 4 - new password > 50 characters
    tbl_params(4).old_pwd := 'short_and_to_the_point';
    tbl_params(4).new_pwd := 'just leaning on the keyboard until i have printed over 50 characters zzzzz';
    
    -- Test 5 - parameters are valid but we're not connected through APEX...
    
    tbl_params(5).old_pwd := 'Valid_pwd';
    tbl_params(5).new_pwd := 'anotherboringpassword';
    
    --
    -- Execute the tests
    --
    for i in 1..tbl_params.count loop
        begin
            change_apex_user_pwd_pr
            (
                i_old_pwd => tbl_params(i).old_pwd,
                i_new_pwd => tbl_params(i).new_pwd
            );
            dbms_output.put_line('Test '||i||' - Somthing has gone wrong - no error !');
        exception
            when others then
                dbms_output.put_line('Test '||i||' Error : '||sqlerrm);
        end;
    end loop;
end;
/

Running this gives us :

Test 1 Error : ORA-20000: Both the Old Password and the New Password must be specified
Test 2 Error : ORA-20000: Both the Old Password and the New Password must be specified
Test 3 Error : ORA-20001: Passwords must not exceed 50 characters in length.
Test 4 Error : ORA-20001: Passwords must not exceed 50 characters in length.
Test 5 Error : ORA-20002: This function can only be called from APEX

PL/SQL procedure successfully completed.

To test the rest of the function, we will of course, need to be connected via APEX.

The Change Password Page

Now we come to the page we will be using to call the procedure we’ve just created.
The page will have :

  • a password field for the application user to enter their current password
  • a password field for the application user to enter their new password
  • and another one for them to re-type it
  • some validation that the new password and confirm password matches
  • a button to call the change password procedure
  • a field to present a message to the user after the password change call

Sounds simple (dangerous) enough…

In Application Builder hit the Create Page button…

select Blank Page ….

In the Page Attributes…

Page Alias : change_db_pwd

change_pwd1

In the Page Name …

Name : Change My Password
HTML Region1 : change password

change_pwd2

In Tab Options…

Tab Options : Use an existing tab set and create a new tab within the existing tab set
New Tab Label : Change Password

change_pwd3

…and hit Finish.

Now Edit the Page.

Create a new field with an Item Type of Password :

old_pwd1

In the Display Position and Name screen,

Item Name : PX_OLD_PWD (where X is the number of the page you’re editing).

old_pwd2

In the Item Attributes Screen :

Label : Current Password
Field Width : 50

old_pwd3

In the Settings Screen –

Value Required : Yes
Submit when Enter pressed : No

old_pwd4

In the Source Screen :

Source Used : Always, replacing any existing session state

Hopefully, the APEX5 Graphical Page Designer will result in fewer screenshots being required in future !
Hopefully, the APEX5 Graphical Page Designer will result in fewer screenshots being required in future !

And hit Create Item.

Now create two further fields with the same properties except :

PX_NEW_PWD has a label of New Password
PX_CONFIRM_PWD has a label of Confirm New Password

Next, we create a Display Only field called PX_MESSAGE.
We’ll use this to provide feedback to the user.
We define this with no label so that it doesn’t show up on the screen, until it’s populated.

Now we’ve got all of the fields on the page the next step is to create the Change Password button :

Accept the defaults for Button Region and Button Position.

In the Button Attributes Page :

Button Name : change_pwd_btn
Label : Change Password

cpwd_btn1

Then just hit Create Button.

Finally, we need to add a Dynamic Action to validate that the values in PX_NEW_PWD and PX_CONFIRM_PWD are not null and identical, and then to call the Procedure.

NOTE – I daresay any APEX experts reading this may have a better way of doing this !

So, Create a Dynamic Action.

In the Identification Page :

Name : change_pwd_da

da1

In the When Page :
Action : Click
Selection Type : Button
Button : CHANGE_PWD_BTN

da2

In the True Action Page :

Action : Execute PL/SQL Code

The PL/SQL Code is as follows :

begin
    if nvl(:P6_NEW_PWD, 'x') != nvl(:P6_CONFIRM_PWD, 'y')
    then
       :P6_MESSAGE := 'Confirm Password does not match New Password.';
    else
        hr.change_apex_user_pwd_pr
        (
             i_old_pwd => :P6_OLD_PWD,
             i_new_pwd => :P6_NEW_PWD
        );
        :P6_MESSAGE := 'Your password has been changed';
    end if;
exception when others then
      :P6_MESSAGE := SQLERRM;
end;

Page Items to Submit : P6_OLD_PWD,P6_NEW_PWD,P6_CONFIRM_PWD,P6_MESSAGE
Page Items to Return : P6_MESSAGE

da3

Click Create Dynamic Action.

Now to test.
I’m connected as PLUGGER and I want to change my password.
So, I click on the Change Password Tab and I see :

cpwd_page1

If the new and confirm password fields don’t match, I get an error from the Dynamic Action itself, before it calls the procedure :

Someone's having a fat-finger moment
Someone’s having a fat-finger moment

When I manage to get it right and am rewarded with :

pwd_changed

Invoking the Change Password Programatically

All that remains now is for us to arrange for the user to be re-directed to the change password page when they connect and their password is near expiry.

The password expiry_date is available in the DBA_USERS view so we need to grant SELECT on this to HR :

grant select on sys.dba_users to hr
/

As I’m re-directing them to a page that belongs specifically to the current application, I’m going to put the re-direction in the application itself.
So, I’m going to add a Branch to the Home Page.
Once again we need to pause here for the APEX gurus to explain the proper way to do this !

Edit the Home Page and Create a Branch…

In Branch Attributes

Name : pwd_change_br
Branch Point : On Load : Before Header

br_cpw1

In Target

Page : the number of the Change Password Page ( 6 in my case)

In Branch Conditions

Condition Type : Exists( SQL query returns at least one row)
In Expression 1, enter the query :

select 1
from sys.dba_users
where username = apex_040200.v('APP_USER')
and expiry_date < trunc(sysdate) + 7

This will return 1 if the password is due to expire within the next 7 days.

br_cpw2

and hit Create Branch.

In order to test the branch, I’ve had a bit of a fiddle with the FIXED_DATE parameter so that PLUGGER’s password is now due to expire in less than 7 days.

Now, when I login as plugger…

brtest1

…I go straight to the Change Password Page…

brtest2

Summary

What started off as a fairly short post about Database Authentication Schemes in APEX has grown quite a bit more than I intended.
I believe that the solution to password management, which I have outlined here, is secure.
Obviously, if anyone can spot any flaws in this, I (and anyone reading this), would find it immensly helpful if you could provide reasons/code as to why and how this approach could be exploited.
Whilst the Change Password functionality is something of an overhead in going down the Database Authentication route, the use of database roles, not to mention the RDBMS itself, does mean that this is an approach worth considering when porting older applications to APEX….or maybe it isn’t.
I wonder if there’s a passing Australian who’d like to share their opinion on this ?

Author: mikesmithers

Back in 1993, I discovered that I could get paid money for doing fun stuff with computers. Over the years, I've specialised in Oracle Databases as a developer, a DBA and sometimes, an architect. It's my evil alter-ego - The Antikyte - who writes a blog about my various technical adventures. Yes, that is his Death Star parked in the Disabled Bay. I currently live in the South-West of England with Deb, my long-suffering wife.

15 thoughts on “Implementing a Database Authentication Scheme in APEX”

  1. I have to start by saying I don’t consider myself an expert in Apex, or Aussie rules. I happen to think all the football codes are somewhat ridiculous in their unique ways đŸ™‚

    I think your technique of reusing the Oracle database authentication and roles is quite good, esp. if you need to continue supporting a legacy application at the same time as Apex. Great stuff!

    With regards to the quote restriction in passwords: in Oracle there is no restriction on using single quotes (‘), but there is a restriction on using double quotes (“). To be safe, all you need to do is surround the user-entered passwords with double-quotes, e.g.:

    execute immediate ‘alter user ‘||l_user||’ identified by “‘||i_new_pwd||'” replace “‘||i_old_pwd||'”‘;

    In fact, since the alter user command treats the password as an identifier, it’s preferable to delimit it with double-quotes anyway because without double-quotes it’s quite a bit more restrictive (see http://docs.oracle.com/cd/E11882_01/network.112/e36292/guidelines.htm#DBSEG10005).

    If the user tries to use a single-quote in their password, it will work fine and there is no sql injection problem. If they try a double-quote, Oracle will actually raise an exception, so again no sql injection problem. See e.g. http://www.pythian.com/blog/oracle-passwords-how-to-use-punctuation-symbols/.

    Like

  2. Quite a comprehensive post!

    Footy, a sport where you can be in front by a behind…

    Main thing I think worth mentioning, having been working on a system using database authentication for a while – the role query needs to be hierarchical, since you could receive HR_USER via another role, ie:

    WHERE granted_role = p_role
    START WITH grantee= p_user
    CONNECT BY grantee = PRIOR granted_role;

    Like

  3. I have been struggling to configure Apex to use Novell LDAP… it occurred to me why can’t I use Database account authentication and searched online, to my surprise as you mentioned only handful of articles explains about that. Your article is very very helpful. Thanks for posting. I didn’t use the Change Password logic. We use Ellucian-Banner ERP; we change password via that – it is a built-in feature, so I didn’t want to replicate it.

    Like

    1. Christilda,

      glad to hear that this post was of some help.
      If you are using Database Roles in your solution then it’s probably worth noting Scot’s comment about making sure that the role query is hierarchical ( if you haven’t already).

      Mike

      Like

  4. Mike,
    Excellent post, and I’m glad it was the first in my search results on Google! I’m on my first APEX project as lead BA (I usually work on .Net or Java applications and I am familar with other IDEs even “declarative” IDEs as Oracle likes to profess) and for all intents and purposes, the scenario at my client is the same – a migration of Forms apps to APEX. The question they had for me is:
    Is there a way for APEX to log in as the actual user and not Anonymous or Public User so that DBAs can manage sessions (by user name) in Oracle’s Enterprise Manager? I said they might have to start using the APEX Admin dashboard but they weren’t too happy with that.

    Having said, we were also looking for the solution you posted; re-using the DB users and roles that exist for the forms applications. The PW reset was just gravy.

    Looking forward to reading other posts on your blog.

    HMB

    Like

  5. what is I have the following tables:
    scott.orders
    tiger.orders

    I want to be able to connect as scott and manipulate scott data and biseversa.

    is that possible?

    Like

    1. Juan,

      yes it is possible.
      Is this an Apex specific question or a general Oracle one ?

      If it’s about Oracle generally, then each schema owner will have full privileges on their table by default.
      So, SCOTT will be able to manipulate data in SCOTT.ORDERS.

      HTH,

      Mike

      Like

  6. I might be missing something here. My understanding is that the ALTER USER IDENTIFIED BY REPLACE only works for a user replacing their own password. In order for one user to set the password of another user, the ALTER USER privilege is required, and if you have that privilege, the “REPLACE ” is ignored.

    My testing certainly supports that position. How is it you application seemingly works?

    Like

    1. Shane,

      If you run the ALTER USER command stand alone, it behaves as you describe.
      However, things are a bit different if you’re executing a stored program. To demonstrate this…

      First of all, connected to 11g XE as SYSDBA…

      -- create a password verification function
      
      create or replace function pwd_verify
      (
          username in varchar2,
          new_password in varchar2,
          old_password in varchar2
      )
          return boolean
      is
      begin
          return true;
      end;
      /
      
      -- create a profile to assign the password verify function to a user.
      
      create profile test_profile limit password_verify_function pwd_verify
      /
      
      -- now create the user marvin
      
      grant create session to marvin identified by forty_two
      /
      
      -- and allocate the profile to marvin
      
      alter user marvin profile test_profile
      /
      
      

      Next, connected as a user with the ALTER USER privilege ( mike in this case), create a password reset procedure which includes the REPLACE clause and grant it to marvin :

      create or replace procedure reset_password
      (
          i_old_password in varchar2,
          i_new_password in varchar2
      )
          authid current_user
      is
      begin
          execute immediate 'alter user '||user||' identified by '||i_new_password||' replace '||i_old_password;
      end;
      /
      
      grant execute on reset_password to marvin
      /
      
      

      Next, we can connect as marvin and do some tests.

      -- try calling the proc without specifying a value for the current password
      exec mike.reset_password(null, 'dentarthurdent')
      
      Error starting at line : 1 in command -
      BEGIN mike.reset_password(null, 'dentarthurdent'); END;
      Error report -
      ORA-00988: missing or invalid password(s)
      ORA-06512: at "MIKE.RESET_PASSWORD", line 8
      ORA-06512: at line 1
      00988. 00000 -  "missing or invalid password(s)"
      *Cause:    
      *Action:
      
      

      Now try specifying any old junk as the old password…

      exec mike.reset_password('dingos_kidneys', 'dentarthurdent')
      
      Error starting at line : 1 in command -
      BEGIN mike.reset_password('dingos_kidneys', 'dentarthurdent'); END;
      Error report -
      ORA-28008: invalid old password
      ORA-06512: at "MIKE.RESET_PASSWORD", line 8
      ORA-06512: at line 1
      28008. 0000 -  "invalid old password"
      *Cause:    old password supplied is wrong; Hence user cannot be
                 authenticated using old password
      *Action:   Supply the correct old password for authentication
      
      

      …and now with the correct current password…

      exec mike.reset_password('forty_two', 'dentarthurdent')
      
      PL/SQL procedure successfully completed.
      

      To check, logout and login again…

      $ sqlcl marvin/dentarthurdent
      
      SQLcl: Release 4.2.0 Production on Thu May 04 07:49:57 2017
      
      Copyright (c) 1982, 2017, Oracle.  All rights reserved.
      
      Connected to:
      Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
      
      

      Finally, just to prove that the REPLACE does appear to be ignored if connected as MIKE to change another user’s password :

      alter user marvin identified by forty_two replace dingos_kidneys
      /
      
      User MARVIN altered.
      
      

      HTH,

      Mike

      Like

  7. mike..

    i’ve got error ORA-01031: insufficient privileges everytime i hit the button, i already grant alter user to anonymous, or the owner of the procedure, but it’s still error. do you have any suggestions? thanks before.

    Like

    1. Frederick,

      If you are getting this error when you hit the Change Password button to call the change_apex_user_pwd_pr procedure, there are a couple of things you may want to check :

      • is the procedure owned by the APEX application datatabase schema ( HR in my example) ?
      • does the APEX application database schema ( HR) have alter user privileges ?
      • do you get the same error when you run the procedure directly from SQL*Plus ?

      HTH,

      Mike

      Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.