Unlock oracle accounts without alter user system privilege
I wanted to give non-DBA users (Servicedesk, Operations) the ability to unlock database user accounts. The ‘alter user’ system privilege allows that, but it also gives out a lot more privileges (change password, kill session, alter DBA accounts) than I thought safe.
So instead I created a pl/sql procedure to handle this securely. Usage for it is just:
SQL> set serverout on
SQL> exec sys.unlock_user('username')
CREATE OR REPLACE PROCEDURE sys.unlock_user
/***************************
|| Name : sys.unlock_user
|| Author : Andrew Fraser
|| Date : 02-Jul-2013
|| Purpose : Allow normal users to unlock database accounts
|| Parameters :
|| p_username - the username of the user - case INsensitive
|| Installation notes:
|| This procedure should be owned by sys and the user account then needs execute privilege on this procedure only.
|| Users other than sys could own this procedure if they are given explicit select on dba_role_privs and dba_users.
|| Change History :
***************************/
( p_username IN VARCHAR2 )
AS
v_num_superusers NUMBER := 0 ;
v_num_users NUMBER := 0 ;
v_num_locked NUMBER := 0 ;
BEGIN
/* Check account exists */
SELECT COUNT(*) INTO v_num_users FROM dba_users WHERE username = UPPER ( p_username ) ;
IF v_num_users = 1
THEN
/* Check account is currently locked */
SELECT COUNT(*) INTO v_num_locked FROM dba_users WHERE username = UPPER ( p_username ) and account_status LIKE '%LOCKED%' ;
IF v_num_locked = 1
THEN
/* Check is not a superuser */
SELECT COUNT(*) INTO v_num_superusers FROM sys.dba_role_privs WHERE grantee = UPPER(p_username) AND granted_role IN ('DBA','IMP_FULL_DATABASE') ;
IF v_num_superusers = 0
THEN
/* Unlock account */
EXECUTE IMMEDIATE 'alter user ' || p_username || ' account unlock' ;
dbms_output.put_line ( 'Success : ' || p_username || ' unlocked.' ) ;
ELSE
dbms_output.put_line ( 'Warning : ' || p_username || ' is a superuser account, which cannot be unlocked by this utility, so no action taken.' ) ;
END IF ;
ELSE
dbms_output.put_line ( 'Warning : ' || p_username || ' is not currently locked, so no action taken.' ) ;
END IF ;
ELSE
dbms_output.put_line ( 'Error : ' || p_username || ' does not exist, please check input and try again.' ) ;
END IF ;
END ;
/
sho err
grant execute on sys.unlock_user to my_normal_user ;
Amazing idea 😉