prompt Package body wsgsso... create or replace package body wsgsso is -- c_session_duration_hours constant number := 3; v_current_user wsgsso_user.user_name%type; -- --*********************************************************************** -- -- Check_Session: Check that the supplied client ID is valid -- for the IP address of the client, that the -- session is still valid, and that the client -- ID supplied is not null. -- --*********************************************************************** function check_session( p_client_id in varchar2, p_current_ip in varchar2 ) return boolean is l_stored_ip wsgsso_sessions.ip_address%type; l_check_ok boolean := false; l_expires wsgsso_sessions.expires%type; begin if p_client_id is not null and p_current_ip is not null then l_check_ok := true; -- -- Check wsgsso_sessions table for an existing session -- with the specified ID and the current IP address -- begin select ip_address , expires into l_stored_ip , l_expires from wsgsso_sessions where client_id = p_client_id and ip_address = p_current_ip; exception when no_data_found then l_check_ok := false; l_expires := sysdate; end; -- -- Check that the session has not expired -- if (sysdate > l_expires) then l_check_ok := false; end if; else -- -- Supplied client ID was null, so return fail. -- l_check_ok := false; end if; -- -- If an invalid session was found (either expired or for -- another IP address) then delete that session. -- if not l_check_ok and p_client_id is not null then dbms_transaction.begin_discrete_transaction; delete from wsgsso_sessions where client_id = p_client_id; commit; end if; -- return l_check_ok; end check_session; --*********************************************************************** -- -- Generate_Client_Id: Generate a unique identifier for a new session. -- --*********************************************************************** function generate_client_id return varchar2 is l_client_id wsgsso_sessions.client_id%type; l_is_unique boolean := false; l_count integer; begin -- Generate a pseudo random number. select to_char(to_number( userenv( 'sessionid' ) ) * to_number( to_char( sysdate,'DDDSSSS' ) ) ) into l_client_id from sys.dual; -- -- Check that it is unique in the session table. -- If it is not, increment it until it is. -- while not l_is_unique loop begin select count(*) into l_count from wsgsso_sessions where client_id = l_client_id; exception when no_data_found then l_count := 0; end; if l_count = 0 then -- -- The ID does not already exist in session table -- l_is_unique := true; else l_client_id := to_number(l_client_id) + 1; end if; end loop; return l_client_id; end generate_client_id; -- --*********************************************************************** -- -- Get_User: Returns user name of current user. -- --*********************************************************************** function get_user return varchar2 is begin return v_current_user; end get_user; -- --*********************************************************************** -- -- Get_User_Data: Returns information about a user, specified by -- p_key. This implementation accepts the key -- 'PASSWORD' only. Any other key will result in -- a null string being returned. -- --*********************************************************************** function get_user_data( p_key in varchar2 ) return varchar2 is l_client_id wsgsso_sessions.client_id%type; l_password wsgsso_user.password%type; begin if upper(p_key) = 'PASSWORD' then -- -- Get the current client ID -- l_client_id := WSGL.GetClientID; -- -- Get user's password by joining to user table -- if l_client_id is not null then begin select password into l_password from wsgsso_user u , wsgsso_sessions s where s.client_id = l_client_id and u.user_name = s.user_name; exception when others then l_password := null; end; end if; end if; return l_password; end get_user_data; -- --*********************************************************************** -- -- Build_Request_Url: Build URL to be passed as requested URL to the -- Login Server. It is built from CGI environment -- variables. -- --*********************************************************************** function build_request_url( p_package_name in varchar2 ) return varchar2 is l_protocol varchar2(256); l_request_url varchar2(4000); begin -- -- The mod_plsql gateway will pass in the protocol in the -- environment variable REQUEST_PROTOCOL -- The SERVER_PROTOCOL, which the Apache Listener sets, -- and currently always sets to HTTP/1.0 will not be modified -- by the gateway. -- l_protocol := owa_util.get_cgi_env('REQUEST_PROTOCOL'); -- -- Defualt the protocol to http -- if (l_protocol is null) or (length(l_protocol) = 0) then l_protocol := 'http'; end if; -- -- Build the request URL -- The the request URL is built from CGI environment variables. -- l_request_url:=lower(l_protocol) || '://' || owa_util.get_cgi_env('HTTP_HOST') || owa_util.get_cgi_env('SCRIPT_NAME'); -- -- The if the request was a post then we don't have the posted -- values. We therefore need to go back to the startup page to -- ensure correct entry into the application. -- If the request is a get we have everything we need to go right -- back into our original destination. -- if owa_util.get_cgi_env('REQUEST_METHOD')='POST' then l_request_url:=l_request_url || '/' || p_package_name || '.startup'; -- -- If the package is a module component package we need to -- pass a default value for Z_CHK. Module component package names -- do not -- if INSTR(p_package_name,'$',-1)=0 then l_request_url:=l_request_url || '?Z_CHK=0'; end if; else l_request_url:=l_request_url || owa_util.get_cgi_env('PATH_INFO'); if owa_util.get_cgi_env('QUERY_STRING') is not null then l_request_url:=l_request_url|| '?' || owa_util.get_cgi_env('QUERY_STRING'); end if; end if; -- return l_request_url; end build_request_url; -- --*********************************************************************** -- -- Gen_HTML_Post_Str: Generate a form to redirect to -- l_gen_url. -- --*********************************************************************** function gen_html_post_str( l_gen_url in varchar2 ) return varchar2 is l_htmlstr VARCHAR2(4000); l_ls_url VARCHAR2(2000); l_tname VARCHAR2(100); l_tvalue VARCHAR2(1000); l_len NUMBER; l_qindex NUMBER; l_eq_index NUMBER; begin l_len := length(l_gen_url); l_qindex := instr(l_gen_url, '?'); l_eq_index := instr(l_gen_url, '='); l_ls_url := substr(l_gen_url, 0, l_qindex-1); l_tname := substr(l_gen_url, l_qindex+1, l_eq_index-l_qindex-1); l_tvalue := substr(l_gen_url, l_eq_index+1); l_htmlstr := '' || '
' || '' || '
'; return l_htmlstr; exception when others then htp.p(sqlerrm); end gen_html_post_str; -- --*********************************************************************** -- -- get_cancel_url: Obtain the cancel URL -- This is the URL that the user is returned to if they -- click on the cancel button on the logon form. -- The URL should be a public page that has no security. -- This implementation merely gets the value from the -- WSGSSO_CONSTANTS package that is created when -- wsgsso.sql is run. -- --*********************************************************************** function get_cancel_url return varchar2 is begin return wsgsso_constants.g_cancel_url; end get_cancel_url; -- --*********************************************************************** -- -- get_listener_token: Obtain the listener_token -- The listener token should be the -- : for the listener that this -- application is running on. -- This implementation builds the token from CGI -- environment variables. -- The listener token value *must* be the same as -- the value supplied to the regapp.sql script when -- the enabler configuration info was registered. -- --*********************************************************************** function get_listener_token return varchar2 is l_listener_token VARCHAR2(1000); begin l_listener_token := owa_util.get_cgi_env('SERVER_NAME') || ':' || owa_util.get_cgi_env('SERVER_PORT'); return l_listener_token; end get_listener_token; -- --*********************************************************************** -- -- SSO_Redirect: Cause a redirect to the login server -- --*********************************************************************** procedure sso_redirect( p_package_name in varchar2 ) is l_html_post VARCHAR2(32000); l_gen_redirect_url VARCHAR2(32000); begin -- -- Generate the redirect URL -- l_gen_redirect_url := wwsec_sso_enabler_private.generate_redirect ( p_lsnr_token => get_listener_token(), urlrequested => build_request_url(p_package_name), urloncancel => get_cancel_url() ); -- -- The l_gen_redirect_irl is usually large url which might -- get truncated by browser. -- Instead of using owa_util.redirect_url, we will use -- HTTP POST for sending redirect -- For mobile applications etc. it might not be possible to use HTTP -- POST since it may not support html hidden form parameter -- owa_util.redirect_url(l_gen_redirect_url); -- Note however that redirecting by HTTP POST does not work with some -- mobile devices that do not support the javascript on_load event. -- Using the owa_util.redirect will work with mobile devices. -- l_html_post := gen_html_post_str(l_gen_redirect_url); htp.p(l_html_post); -- end sso_redirect; -- --*********************************************************************** -- -- package_permission: Check that the user p_username has a -- responsibility that allows it to access the -- package p_package_name. Return true if the user -- does have access, false otherwise. -- --*********************************************************************** function package_permission ( p_username in varchar2, p_package_name in varchar2 ) return boolean is l_rows integer := 0; begin -- -- Lookup the roles that are common to the package and -- user passed in. -- begin select count(*) into l_rows from wsgsso_userresp ur , wsgsso_perm pm where ur.user_name = p_username and pm.package_name = p_package_name and ur.responsibility_name = pm.responsibility_name; exception when no_data_found then l_rows := 0; end; -- -- If no rows were found the user does not have permission -- if l_rows = 0 then return false; end if; return true; end; -- --*********************************************************************** -- -- Security_Check: Returns true if the currently logged on user is -- allowed access to the specified package. If not, -- will return false. This function will also provide -- a means for the user to log on. -- --*********************************************************************** function security_check( p_package_name in varchar2 ) return boolean is l_client_id wsgsso_sessions.client_id%type; l_current_ip wsgsso_sessions.ip_address%type; begin -- -- Get the current client ID and IP address -- l_client_id := WSGL.GetClientID; l_current_ip := owa_util.get_cgi_env('REMOTE_ADDR'); if l_client_id is not null then -- -- Call check_session to ensure current session -- is valid -- if check_session( l_client_id, l_current_ip ) then -- -- Get user name -- begin select user_name into v_current_user from wsgsso_sessions where client_id = l_client_id; exception when others then v_current_user := null; end; else -- -- No valid session was found so redirect to the login-server -- for authentication. -- sso_redirect(p_package_name); return false; end if; -- -- Check that the user has correct package permission -- if package_permission( v_current_user, p_package_name ) then -- -- User is logged on and has permission to access -- the specified package. -- return true; else -- -- User is logged on but does not have permission -- to access the specified package -- show_no_access; return false; end if; else -- -- No current client ID, so redirect to the login server -- for authentication. -- sso_redirect( p_package_name ); return false; end if; end security_check; -- --*********************************************************************** -- -- Valid_Username: returns true if username is a known username. -- --*********************************************************************** function valid_username( p_username in varchar2 ) return boolean is l_rows number; begin begin select count(*) into l_rows from wsgsso_user where user_name = p_username; exception when no_data_found then l_rows := 0; end; if l_rows = 0 then return false; end if; return true; end valid_username; -- --*********************************************************************** -- -- Update_Session: updates the wsgsso_session table with information -- passed. Must pass true for z_direct_call. This -- prevents update_sesssion being called from a URL -- request because a boolean cannot be passed. -- The value of p_client_id passed back is the new -- client session id. This maybe different from the -- value passed in if that did not correspond to a -- currently valida row in wsgsso_sessions table. -- If p_username does not correspond to a known -- username the invalid_user exception is raised. -- --*********************************************************************** procedure update_session( p_client_id in out varchar2, p_ip_address in varchar2, p_username in varchar2, z_direct_call in boolean default false ) is pragma autonomous_transaction; begin if z_direct_call then -- -- Verify that the user is a valid user on this application. -- if not valid_username(p_username) then -- -- User is not a known user. They do not have access. -- raise wsgl.invalid_user; end if; -- -- Check that current client ID is valid -- if p_client_id is not null and check_session( p_client_id, p_ip_address ) then -- -- Update the session table for this client with -- the new user -- dbms_transaction.begin_discrete_transaction; update wsgsso_sessions set user_name = p_username where client_id = p_client_id; commit; else -- -- Current client ID is not valid so create a new one -- p_client_id := generate_client_id; -- -- Create new session in session table -- dbms_transaction.begin_discrete_transaction; insert into wsgsso_sessions( client_id, user_name, ip_address, expires ) values( p_client_id, p_username, p_ip_address, (sysdate + (c_session_duration_hours /24)) ); commit; end if; end if; exception when others then raise; end update_session; -- --*********************************************************************** -- -- Sign_On: Recieves encoded url login cookie from login server and -- uses it to create wsgsso session. -- --*********************************************************************** procedure sign_on( urlc in varchar2 ) is l_sso_user_name wsgsso_sessions.user_name%type; l_ip_address wsgsso_sessions.ip_address%type; l_sso_time_remaining VARCHAR2(1000); l_site_time_stamp VARCHAR2(1000); l_url_requested VARCHAR2(1000); l_unused_param VARCHAR2(1000); l_client_id wsgsso_sessions.client_id%type; begin -- -- Process urlc token passed from login server. -- wwsec_sso_enabler_private.parse_url_cookie ( p_lsnr_token => get_listener_token, encrypted_urlcookie => urlc, ssousername => l_sso_user_name, ipadd => l_ip_address, ssotimeremaining => l_sso_time_remaining, sitetimestamp => l_site_time_stamp, urlrequested => l_url_requested, newsitekey => l_unused_param ); -- -- Get current client ID and IP address -- l_client_id := WSGL.GetClientID; l_ip_address := owa_util.get_cgi_env('REMOTE_ADDR'); -- update_session( l_client_id, l_ip_address, l_sso_user_name, true ); -- -- Write client id to browser as a cookie -- Open http headers but don't close. WSGL.StoreClientID( l_client_id, true, false ); -- -- Redirect user to the requested application url owa_util.redirect_url(l_url_requested); owa_util.http_header_close; -- exception when wsgl.invalid_user then show_no_user; when others then raise; end sign_on; -- --*********************************************************************** -- -- Show_No_Access: Displays message to inform user that they do not -- have access to the application. -- --*********************************************************************** procedure show_no_access is begin htp.htmlOpen; htp.headOpen; htp.title(WSGL.MsgGetText(609,WSGLM.SEC609_ACCESS_DENIED)); htp.headClose; htp.bodyOpen; htp.header(2, WSGL.MsgGetText(609,WSGLM.SEC609_ACCESS_DENIED)); htp.header(4, WSGL.MsgGetText(610,WSGLM.SEC610_NO_PERMISSION)); htp.hr; htp.anchor( curl=>get_cancel_url, ctext=>WSGL.MsgGetText(613,WSGLM.SEC613_HOME)); htp.bodyClose; htp.htmlClose; end show_no_access; -- --*********************************************************************** -- -- Show_No_User: Displays message to inform user that they do not -- have a user on the partner application corresponding -- to their Single-Sign On user. -- --*********************************************************************** procedure show_no_user is begin htp.htmlOpen; htp.headOpen; htp.title(WSGL.MsgGetText(609,WSGLM.SEC609_ACCESS_DENIED)); htp.headClose; htp.bodyOpen; htp.header(2, WSGL.MsgGetText(609,WSGLM.SEC609_ACCESS_DENIED)); htp.header(4, WSGL.MsgGetText(614,WSGLM.SEC614_NO_USER)); htp.hr; htp.anchor( curl=>get_cancel_url, ctext=>WSGL.MsgGetText(613,WSGLM.SEC613_HOME)); htp.bodyClose; htp.htmlClose; end show_no_user; -- --*********************************************************************** -- -- Add_Package_Resp: Adds a respondibility to a package, allowing users -- with that responsibility to access the application. -- This is called from the generated application -- installation script if a value is supplied for -- preference SECRES. -- --*********************************************************************** procedure add_package_resp( p_package_name in varchar2, p_responsibility in varchar2 ) is l_rows integer := 0; begin -- -- Determine whether responsibility exists -- begin select count(*) into l_rows from wsgsso_resp where responsibility_name = p_responsibility; exception when no_data_found then l_rows := 0; end; if l_rows = 0 then -- -- Responsibility does not exist, so create it -- dbms_transaction.begin_discrete_transaction; insert into wsgsso_resp( responsibility_name ) values ( p_responsibility ); commit; end if; -- -- Add row to wsgsso_perm table to grant package access -- to the responsibility, if it does not already exist. -- begin select count(*) into l_rows from wsgsso_perm where responsibility_name = p_responsibility and package_name = p_package_name; exception when no_data_found then l_rows := 0; end; if l_rows = 0 then dbms_transaction.begin_discrete_transaction; insert into wsgsso_perm( responsibility_name, package_name ) values( p_responsibility, p_package_name ); commit; end if; end add_package_resp; --*********************************************************************** -- -- Logoff: Removes the current user's session, logging them off. -- Then causes a redirect to p_refresh_url after p_delay seconds. -- The refresh URL is defaulted to be the SSO Cancel URL. -- In case the device does not support the -- tag, a link to the cancel URL is -- included. -- --*********************************************************************** procedure logoff( p_refresh_url in varchar2 := wsgsso_constants.g_cancel_url, p_delay in varchar2 := '1' ) is l_client_id wsgsso_sessions.client_id%type; l_refresh_url varchar2(4000); begin -- -- Default the p_refresh_url to be the same as the SSO cancel URL. -- if p_refresh_url is not null then l_refresh_url := p_refresh_url; else l_refresh_url := get_cancel_url; end if; htp.htmlOpen; htp.headOpen; htp.title(WSGL.MsgGetText(611,WSGLM.SEC611_LOGGING_OFF)); htp.p(''); htp.headClose; htp.bodyOpen; -- -- Get current client ID -- l_client_id := WSGL.GetClientID; if l_client_id is not null then -- -- Delete session for this client -- dbms_transaction.begin_discrete_transaction; delete from wsgsso_sessions where client_id = l_client_id; commit; end if; htp.header(2, WSGL.MsgGetText(612,WSGLM.SEC612_LOGGED_OFF)); htp.hr; htp.anchor( curl=>l_refresh_url, ctext=>WSGL.MsgGetText(613,WSGLM.SEC613_HOME)); htp.bodyClose; htp.htmlClose; end logoff; --*********************************************************************** -- -- create_User: Creates a user with the specified name and password -- --*********************************************************************** procedure create_user( p_username in varchar2, p_password in varchar2 ) is l_rows integer; begin -- -- Determine whether user already exists -- begin select count(*) into l_rows from wsgsso_user where upper(user_name) = upper(p_username); exception when no_data_found then l_rows := 0; end; if l_rows = 0 then -- -- User does not exist, so create new one -- dbms_transaction.begin_discrete_transaction; insert into wsgsso_user( user_name, password ) values (upper(p_username), nvl(p_password, p_username)); commit; else -- -- User exists, so update password -- dbms_transaction.begin_discrete_transaction; update wsgsso_user set password = nvl(p_password, p_username) where user_name = p_username; commit; end if; end; -- end wsgsso; / show errors