this is my first pl/sql database project, so I am not experienced but need to start somewhere, I am writing a procedure for a update statement of customer table cpw which the user need to enter the username, if that match the cusername column the they will enter the old password, if that match with the cpw of the same row, then the system will update the new password which is prompt and accepted. here's what i have so far. any help will be appreciated.
CREATE OR replace PROCEDURE Changepassword (input_cusername IN VARCHAR,
old_cpw IN VARCHAR,
new_cpw OUT VARCHAR)
IS
DECLARE
c_username customer.cusername%TYPE;
c_cpw customer.cpw%TYPE;
BEGIN
ACCEPT input_cusername VARCHAR(40) prompt 'Username: '
ACCEPT old_cpw VARCHAR(20) prompt 'Enter Your Old Password:'
ACCEPT new_cpw VARCHAR(20) prompt 'Enter Your New Password:'
UPDATE customer
SET cpw = new_cpw
WHERE cusername = input_cusername;
EXCEPTION
WHEN input_cusername <> c_username customer.cusername%TYPE THEN
dbms_output.put_line('no such user exist');
WHEN old_cpw <> cpw FROM customer WHERE cusername = input_cusername THEN
dbms_output.put_line('Password Incorrect');
END;
There are many things wrong with your procedure.
new_cpw should be an IN parameter.
Better use VARCHAR2 data type.
create or replace procedure changePassword (input_cusername IN VARCHAR2,
old_cpw IN VARCHAR2,
new_cpw IN VARCHAR2)
DECLARE your own EXCEPTION and user-defined error message using raise_application_error.
wrong_username EXCEPTION;
wrong_password EXCEPTION;
You MUST first validate the USERNAME entered.
If username is incorrect, then RAISE an EXCEPTION for wrong_username EXCEPTION.
If cusername entered is valid, i.e. if it exists in the table, then verify theold password` entered is correct.
SELECT cpw
INTO var_password
FROM customer
WHERE cusername = input_cusername;
IF UPPER(old_cpw) = UPPER(var_password)
THEN
UPDATE customer SET cpw = new_cpw WHERE cusername = input_cusername;
ELSE
RAISE wrong_password;
END IF;
Handle the exceptions gracefully.
EXCEPTION
WHEN wrong_username THEN
raise_application_error (-20001,'You have entered an incorrect username');
WHEN wrong_password THEN
raise_application_error (-20001,'You have entered an incorrect password');
You MUST always have validations on the password security and levy some password rules. For example, LENGTH of password etc.
Although there are a few minor issues with your code (such as those pointed out by Lalit), the main thing you need to address first is what you actually want this code to do.
You have mixed some ACCEPT commands in your PL/SQL, but these are SQL*Plus commands that are only intended for interactive scripts; they cannot be included within PL/SQL. PL/SQL is not an interactive language; it is designed to be called from some other process (whether from within an interactive SQL*Plus script, or from a front-end interface such as Apex).
Related
Is there a way to automatically create a user in my application based on values in a table?
So I have a table named EMP as employee in that table I have attributes name, surname, username and password. What do I need to do to automatically create a user account in my application when a new employee is inserted into the database?
I have no idea where to start, is that done in processes or is there a pre-built feature that covers this?
Is there a sample code that everyone just uses and adapts to their needs?
In such a case, maybe it would be better to use custom authentication scheme instead of default, built-in Apex authentication.
How to create new users? Just insert them into the table. It would be a good idea to create a package which contains procedures used to manipulate with users' data.
You shouldn't store passwords as text for security reasons.
For example:
procedure p_create_user (p_name in varchar2, p_surname in varchar2,
p_username in varchar2, p_password in varchar2)
is
l_hash raw (2000);
begin
l_hash :=
dbms_crypto.hash (
utl_i18n.string_to_raw (p_password, 'AL32UTF8'),
dbms_crypto.hash_sh1);
insert into emp (name, surname, username, password)
values
(p_name, p_surname, p_username, l_hash);
end;
In Apex, authentication scheme requires you to create your own function (and set its name into the "Authentication Function Name" property) which accepts username/password combination and returns Boolean: TRUE (if credentials are valid) or FALSE (if not). Note that parameters' names MUST be p_username and p_password.
Use code similar to that in previous procedure:
function f_auth (p_username in varchar2, p_password in varchar2)
return boolean
is
l_pwd emp.password%type;
begin
select password
into l_pwd
from emp
where username = p_username;
return l_pwd = dbms_crypto.hash (
utl_i18n.string_to_raw (p_password, 'AL32UTF8'),
dbms_crypto.hash_sh1);
end;
That's all you need for basic functionality. You can add another procedure to let users change their password etc.
I am new at Oracle development using oracle 10g Forms.
I am trying to write this code for login purpose. When user enters wrong password, it gives alert in popup window. Also, when user enters wrong username, it raise an alert.
I wrote this code but I am unable to correct it.
DECLARE
un VARCHAR (15);
pwd VARCHAR (15);
BEGIN
--- Dynamic altert properties code start--
SET_ALERT_PROPERTY ('LOGIN_ALERTS', TITLE, 'Security Altert');
SET_ALERT_PROPERTY ('LOGIN_ALERTS',
ALERT_MESSAGE_TEXT,
'Wrong Username?');
SET_ALERT_PROPERTY ('LOGIN_ALERTS',
ALERT_MESSAGE_TEXT,
'Wrong Password?');
SELECT username, users_password
INTO un, pwd
FROM MENU_USERS
WHERE username = :TXT_USERNAME;
IF un = :TXT_USERNAME
THEN
IF pwd = :TXT_PWD
THEN
CALL_FORM ('F:\ISMS\INV\inv_stock.fmx');
ELSE
SHOW_ALERT ('LOGIN_ALERTS');
END IF;
ELSE
SHOW_ALERT ('LOGIN_ALERTS');
END IF;
END;
Alerts are OK, but - consecutive message calls will do as well. This example shows how to use that approach.
Code you wrote is kind of "wrong" as it doesn't handle "wrong username" situation. When that happens, select won't return anything and will raise the no_data_found exception which should be handled.
Here you go:
declare
pwd varchar2(15);
begin
select users_password
into pwd
from menu_users
where username = :txt_username;
-- if TXT_USERNAME value exists, SELECT will return its password
if pwd = :txt_pwd then
call_form('whichever form you want');
else
message('Wrong password');
message('Wrong password');
raise form_trigger_failure;
end if;
exception
when no_data_found then
-- SELECT didn't return anything, but raised an exception
message('Wrong username');
message('Wrong username');
end;
i am trying to create a package called MSGG_SESSION with a procedure authenticate that accepts two VARCHAR2 parameters for username and password. i am suppose to put an package-private NUMBER variable for the current person ID.If "authenticate" matches a username and password in MSGG_USER , put the matching PERSON_ID in the new variable. Add a function get_user_id to the package that returns the value of the variable holding the person ID.
but i get two erros saying table or view does not exits starting at the second is before not_authenticated_exception
and sql statement ignored starting at priv_number varchar2(100).
CREATE OR REPLACE PACKAGE MSGG_SESSION IS
PROCEDURE AUTHENTICATE (USERNAME_to_auth IN VARCHAR2, PASSWORD_to_use IN VARCHAR2);
FUNCTION AUTHENTICATED_USER RETURN VARCHAR2;
END MSGG_SESSION;
/
create or replace package body msgg_session is
priv_number varchar2(100);
procedure authenticate (username_to_auth in varchar2, password_to_use in varchar2)
is
not_authenticated exception;
begin
select username
into priv_number
from user_password
where lower(username) = lower(username_to_auth)
and password = password_to_use;
exception
when no_data_found then
begin
raise not_authenticated;
exception
when not_authenticated then
raise_application_error(-20000, 'Not authenticated');
end;
when others then
raise;
end authenticate;
function authenticated_user
return varchar2
is
begin
return null;
end;
function get_user_id
return varchar2
is
begin
return priv_number;
end get_user_id;
end msgg_session;
/
You don't provide table DDL or the line number of the error message so it's not clear why you would get ORA-00942: table or view does not exist. Check the spelling of the table, make sure the table and the package are in the same schema and nothing is defined in double-quotes (e.g. user_password is not the same as "user_password").
Assuming that the table looks something like this:
create table user_password
( user_id integer constraint user_password_pk primary key
, username varchar2(30) not null constraint user_password_username_uk unique
, password varchar2(30) not null );
with sample test data:
insert into user_password (user_id, username, password)
values (1, 'ndubizuacn', 'Kittens');
A fixed version of your package would look like this:
create or replace package msgg_session as
procedure authenticate
( username_to_auth in user_password.username%type
, password_to_use in user_password.password%type );
function get_user_id
return user_password.user_id%type;
end msgg_session;
/
create or replace package body msgg_session as
priv_number user_password.user_id%type;
procedure authenticate
( username_to_auth in user_password.username%type
, password_to_use in user_password.password%type )
is
begin
select user_id into priv_number
from user_password
where lower(username) = lower(username_to_auth)
and password = password_to_use;
exception
when no_data_found then
raise_application_error(-20000, 'Not authenticated');
end authenticate;
function authenticated_user
return varchar2
is
begin
return null;
end authenticated_user;
function get_user_id
return user_password.user_id%type
is
begin
return priv_number;
end get_user_id;
end msgg_session;
/
Test:
begin
msgg_session.authenticate('ndubizuacn', 'Kittens');
dbms_output.put_line(msgg_session.get_user_id);
end;
/
Assuming dbms_output is enabled, this prints the value 1.
Using a global variable for something like this doesn't make a great interface, but it's a requirement of the assignment so I guess it shows how to use one. Same goes for needing to make two calls - perhaps you could expand your authenticated_user function to provide an alternative interface (pass in user and password, get back user_id all in one shot).
Storing passwords in plain text is an obvious security risk, and it is sometimes said that you should never use any online service that can send you your password if you forget it (you don't see that too often these days, but it used to be quite common). It would be more secure not to store the password at all but instead store ora_hash(upper(username)||'~'||password)), so for example for username ndubizuacn and password Kittens you would store 2160931220. Then your authentication function might be something like:
function authenticated_user
( username_to_auth in user_password.username%type
, password_to_use in user_password.password%type )
return user_password.user_id%type
is
l_user_id user_password.user_id%type;
begin
select user_id into l_user_id
from user_password
where username = username_to_auth
and password_hash = ora_hash(upper(username_to_auth)||'~'||password_to_use);
return l_user_id;
exception
when no_data_found then
raise_application_error(-20000, 'Not authenticated');
end authenticated_user;
I have created procedure to check and validate username and password, even when I provide correct password I will receive always exception error. I tried different thing inside the procedure but results would be the same.
create or replace
PROCEDURE member_ck_sp
(p_uname IN VARCHAR2,
p_pass IN VARCHAR2,
p_name OUT VARCHAR2,
p_cookie OUT VARCHAR2)
IS
CURSOR CUR_CHECK IS
SELECT USERNAME, PASSWORD,FIRSTNAME||''||LASTNAME, COOKIE
FROM bb_shopper;
lv_check_txt VARCHAR2(100);
BEGIN
FOR rec_check IN cur_check LOOP
IF p_uname = rec_check.username
AND p_pass = rec_check.PASSWORD THEN
lv_check_txt := 'Pass';
ELSE lv_check_txt := 'Fail';
END IF;
END LOOP;
IF lv_check_txt = 'Pass' THEN
SELECT FIRSTNAME||''||LASTNAME, COOKIE
INTO p_name, p_cookie
FROM bb_shopper
WHERE USERNAME = P_UNAME
AND password = p_pass;
dbms_output.put_line(p_name||' '|| p_cookie);
ELSE raise no_data_found;
END IF;
--dbms_output.put_line(p_name||' '|| p_cookie);
EXCEPTION
WHEN no_data_found THEN
dbms_output.put_line('Please reneter credentials');
END;
And block to check code:
DECLARE
lv_username_txt bb_shopper.username%TYPE := 'rat55';
lv_password_txt bb_shopper.PASSWORD%TYPE := 'kile';
lv_name_txt VARCHAR2(200);
lv_cookie_txt bb_shopper.cookie%TYPE;
BEGIN
member_ck_sp(lv_username_txt,lv_password_txt,lv_name_txt,lv_cookie_txt);
--DBMS_OUTPUT.PUT_LINE('User name is '||lv_name_txt||' and
cookie '||lv_cookie_txt);
END;
Your problem is the opening LOOP reads all the records in bb_shopper. One of those records presumably matches the entered credentials. However, unless the last record read is the matching one, you will exit the loop with lv_check_txt = 'Fail'. And that's why you always fail the test in the subsequent IF and get no_data_found.
The solution seems quite simple: ditch the loop and just validate the passed parameters.
create or replace
PROCEDURE member_ck_sp
(p_uname IN VARCHAR2,
p_pass IN VARCHAR2,
p_name OUT VARCHAR2,
p_cookie OUT VARCHAR2)
IS
BEGIN
SELECT FIRSTNAME||''||LASTNAME, COOKIE
INTO p_name, p_cookie
FROM bb_shopper
WHERE USERNAME = P_UNAME
AND password = p_pass;
--dbms_output.put_line(p_name||' '|| p_cookie);
EXCEPTION
WHEN no_data_found THEN
raise_application_error(-20000, 'Please re-enter credentials');
END;
I haven't looked at PL\SQL in a long time. However, my first suggestion would be to look at your test data:
SELECT * FROM bb_shopper where username = 'rat55';
A few things to keep in mind:
The last line in the block to check code was probably meant to be commented out. It contains a quotation mark left open and a close bracket without an opening bracket. That can't help.
I'll take a different tack on this one. I see one potential error that overrides anything regarding the syntax and functionality, and that is:
I really really REALLY hope you are not planning on storing cleartext passwords in a database table.
Do not ever do this....ever. Please tell us that this routine already has the password salted/hashed before making it to this routine and table. Otherwise, this is the first thing you should looking at fixing before anything else.
I need to get the output in uu in accordance with value passed through the prompt
create or replace procedure chklg( uu out logn.username%TYPE
, pass in logn.password%TYPE)
is
begin
select username into uu from logn where password=pass;
end;
I tried executing the above procedure this way:
begin
chklg(:pass);
end
By definition a procedure doesn't return anything. You're looking for a function.
create or replace function chklg ( p_pass in logn.password%TYPE
) return varchar2 is -- assuming that logn.username%TYP is a varchar2
l_uu logn.username%type;
begin
select username into l_uu from logn where password = p_pass;
return l_uu;
-- If there-s no username that matches the password return null.
exception when no_data_found then
return null;
end;
I'm slightly worried by this as it appears as though you're storing a password as plain text. This is not best practice.
You should be storing a salted and peppered hash of your password next to the username, then apply the same salting, peppering and hashing to the password and select the hash from the database.
You can execute the function either of the following two ways:
select chklg(:pass) from dual
or
declare
l_pass logn.password%type;
begin
l_pass := chklg(:pass);
end;
/
To be complete Frank Schmitt has raised a very valid point in the comments. In addition to you storing the passwords in a very dangerous manner what happens if two users have the same password?
You will get a TOO_MANY_ROWS exception raised in your SELECT INTO .... This means that too many rows are returned to the variable. It would be better if you passed the username in as well.
This could make your function look something like the following
create or replace function chklg (
p_password_hash in logn.password%type
, p_username in logn.username%type
) return number
/* Authenticate a user, return 1/0 depending on whether they have
entered the correct password.
*/
l_yes number := 0;
begin
-- Assumes that username is unique.
select 1 into l_yes
from logn
where password_hash = p_password_hash
and username = p_username;
return l_yes;
-- If there-s no username that matches the password return 0.
exception when no_data_found then
return 0;
end;
If you're looking to only use a procedure (there's no real reason to do this at all as it unnecessarily restricts you; you're not doing any DML) then you can get the output parameter but you have to give the procedure a parameter that it can populate.
In your case it would look something like this.
declare
l_uu logn.username%type;
begin
chklg(l_uu, :pass);
dbms_output.put_line(l_uu);
end;