Re: Dynamic SQL problem

From: Thomas J. Kyte <tkyte_at_us.oracle.com>
Date: 2000/05/17
Message-ID: <8furd5$r36$1_at_nnrp1.deja.com>


In article <8fubja$7r0$1_at_nnrp1.deja.com>,   sigdock_at_my-deja.com wrote:
> Hi all,
>
> I am trying to build a procedure which can be used to change the
> password of a user. To verify that a user can only change his own
> password I'm using a connect-statement in a DBMS_SQL statemant.
 However
> this results in a ORA-0900, invalid statement, error. Does anybody
 have
> any idea how this can be solved?
>
> t.i.a.

well, you cannot connect in plsql as you are already connected and there is no way to create another connection....

one way to do this is to ignore the "old_password" all together -- the user is already logged in -- they already supplied it. You know they know their password by the very fact they are running this stored procedure.

If you are trying to make it so that you verify at the moment in time the procedure is run that the person running it is who they are logged in as (to avoid someone walking by someones logged in terminal and changing their password), you could use this procedure:

create or replace package check_password as

    function is_valid( p_username in varchar2, p_password in varchar2 )     return boolean;

end;
/

create or replace package body check_password as

g_alter_cmd varchar2(50)
default 'alter user $U identified by $P';

g_reset_cmd varchar2(50)
default 'alter user $U identified by values ''$P''';

function rep_up( p_str in varchar2, p_u in varchar2, p_p in varchar2 ) return varchar2
is
begin

      return replace(replace(p_str,'$U',p_u),'$P',p_p); end;

procedure execute_immediate( stmt in varchar2 ) as

    exec_cursor integer default dbms_sql.open_cursor;     rows_processed number default 0;
begin

    dbms_sql.parse(exec_cursor, stmt, dbms_sql.native );
    rows_processed := dbms_sql.execute(exec_cursor);
    dbms_sql.close_cursor( exec_cursor );
exception

    when others then

      if dbms_sql.is_open(exec_cursor) then
        dbms_sql.close_cursor(exec_cursor);
      end if;
      raise;

end execute_immediate;

function is_valid( p_username in varchar2, p_password in varchar2 ) return boolean
is

    l_dba_users1        dba_users%rowtype;
    l_dba_users2        dba_users%rowtype;
begin

    select * into l_dba_users1

      from dba_users
     where username = upper(p_username);

    execute_immediate( rep_up( g_alter_cmd, p_username, p_password ) );

    select * into l_dba_users2

      from dba_users
     where username = upper(p_username);

    if ( l_dba_users1.password <> l_dba_users2.password )     then

        execute_immediate( rep_up( g_reset_cmd, p_username,
                                   l_dba_users1.password));
    end if;

    return l_dba_users1.password = l_dba_users2.password; end is_valid;

end;
/

it works by checking the digested password that currently exists with the new digested password that would exist if their password was the one they supplied... If the two do not match -- it puts back the old password.

This procedure has (unavoidable) a flaw in that if the database goes down AFTER we alter the user and BEFORE we put it back -- we may lose that users password. The window is very small but exists. There is no real way to avoid it.

So, your procedure below would become simply:

....
 if ( check_pass.is_valid( USER, old_password ) )  then

     dbms_sql.parse( ..., 'alter user ' || user || ' identified by ' || new_password );
  end if;
...

>
> B.J. Meinders
> Oracle DBA
> ASR-ICT
>
> Source Code:
>
> Create or replace procedure
> CHANGE_PASSWORD ( OldPass IN VARCHAR2,
> NewPass IN VARCHAR2,
> NewPass_Ver IN VARCHAR2 )
> As
> --
> -- Declarations
> --
> Cursor_Num INTEGER;
> Length_Passw INTEGER;
> Cmd_String VARCHAR2(100);
> Cursor USERNAME_CURSOR is
> select User
> from DUAL;
> UserNm VARCHAR2(32);
> --
> Cursor DATABASE_CURSOR is
> select name
> from v$database;
> Dbasenm VARCHAR2(4);
> --
> Begin
> --
> -- Verify new password
> --
> If NewPass <> NewPass_Ver then
> DBMS_OUTPUT.PUT_LINE('Password verification error, password not
> changed');
> Else
> Begin
> --
> -- Get username
> --
> open USERNAME_CURSOR;
> fetch USERNAME_CURSOR
> into UserNm;
> close USERNAME_CURSOR;
> Exception
> When others then
> Raise_Application_Error(-20001,'Error while fetching
 username');
> End;
> --
> Begin
> --
> -- Get databasename
> --
> open DATABASE_CURSOR;
> fetch DATABASE_CURSOR
> into DbaseNm;
> close DATABASE_CURSOR;
> Exception
> When others then
> Raise_Application_Error(-20002,'Error while fetching
> databasename');
> End;
> --
> Begin
> --
> -- Check old username / password
> --
> Cmd_String := 'connect '||UserNm||'/'||OldPass;
> DBMS_OUTPUT.PUT_LINE(cmd_string);
> Cursor_Num := DBMS_SQL.OPEN_CURSOR;
> DBMS_SQL.PARSE(Cursor_Num, Cmd_String, DBMS_SQL.NATIVE);
> DBMS_SQL.CLOSE_CURSOR(Cursor_Num);
> Exception
> When others then
> DBMS_OUTPUT.PUT_LINE(sqlerrm);
> Raise_Application_error(-20000,'Error during login');
> End;
> --
> -- Change password
> --
> Begin
> Cmd_String:= 'alter user '||UserNm||' identified by '||NewPass ;
> Cursor_Num := DBMS_SQL.OPEN_CURSOR;
> If Cursor_Num is null
> then null;
> Else
> DBMS_SQL.PARSE(Cursor_Num, Cmd_String, DBMS_SQL.Native);
> DBMS_SQL.CLOSE_CURSOR(Cursor_Num);
> End If;
> Exception
> When others then
> Raise_Application_Error(-20003,'Error during changing
> password');
> End;
> End If;
> Exception
> When others then
> DBMS_OUTPUT.PUT_LINE(sqlerrm);
> Raise_Application_Error(-20004,'Unhandled error');
> End;
>
> Sent via Deja.com http://www.deja.com/
> Before you buy.
>

--
Thomas Kyte (tkyte_at_us.oracle.com) Oracle Service Industries
Howtos and such: http://osi.oracle.com/~tkyte/index.html
Oracle Magazine: http://www.oracle.com/oramag
Opinions are mine and do not necessarily reflect those of Oracle Corp


Sent via Deja.com http://www.deja.com/
Before you buy.
Received on Wed May 17 2000 - 00:00:00 CEST

Original text of this message