This content has been marked as final. Show 28 replies
Thanks Scott, I'll take a look at that. I'm just getting into CAS so I don't completely understand it yet, however the way it seems to work is based on a parameter: "ticket=#". All the samples I've seen (in different programming langauges) check this ticket value. If it's null it sends you to the cas login page, with two values your null ticket and a paramter "service" which is the url you're comming from. Once you login at the cas login page it sends you back to the service url with a value for the ticket. If you have a ticket you are authenticated. At any time you can check the cas service to see if the ticket is valid.
I'll update after I look at the ntlm page sentry. Hopefully that will give me enough clues to work this out. I was thinking also that there might be a way to store the ticket as a global APEX variable for each user/session?
-- Edit --
Ok I'm lost. I'm looking at the NTLM Whitepaper. I don't see how this can work. To use the page sentry function I need access to the ticket parameter which is stored in the url. Once I have that ticket value I can check it against cas ... but I don't see how to give the sentry function that parameter. The first time in the ticket would be null, which should then redirect to cas login page. Once the user logs into cas they are redirected back to APEX with a value for ticket, but again I don't see how to pass ticket to the page sentry.
Here's an example of the url as CAS passes me back after login: http://server:port/dad/f?p=102:1&ticket=ST-465-0r7Ke0qEP6YjVNbGGocC
Edited by: Kennedy on Apr 16, 2009 11:28 AM
Thanks Pat. I was actually following the other thread. The Yale-CAS code is specific to integration with Sungard Higher Educations System: Banner and Banner Self Service. We actually use Banner, but the way they have it setup is not quite the way we want it to work. However there code got me started. I am actually making progress.
Using a custom procedure I am able to send theh user out to CAS to login. When they return from cas (meaning they've been authenticated) I can process their userid and create a new apex session id and associate the apex_user with the cas userid. The problem I have is that I don't know how to actually get into APEX from their. I'll post a sample of my procedure tomorrow. I think what I would really like is two entry points.
Entry point 1 would be the normal APEX 101 login page that would use LDAP Authentication.
Entry point 2 would be a custom procedure that could identify the userid and verify it against CAS. Once Authenticated against CAS it would bypass the 101 login page and send the user directly into APEX with a registered Session ID and User ID. The problem I am getting now with this is that I can produce the Session ID and User ID but I can't get the session ID registered. When I use the APEX_CUSTOM_AUTH.IS_SESSION_VALID, I always get a FALSE return.
Anyway I'm starting to ramble. I'll update tomorrow.
I think I have found a way to do this ... but I am still stuck. After more research I believe the best way to handle cas authentication is to use a custom "Session Verify Function" in the Authorization Schema.
CAS assigns a ticket and I can access the user id. So my plan is to use the userid as the p_username and the ticket as the p_password. In my Session Verify Function.
However what I'm stuck on is how to call the apex application and pass in the username and password. Is this even possible?
I did finally get CAS working with APEX - still need to clean up things a bit, and I have yet to deploy it with a real application (vs the "cas test" application). Anyway, starting with Yale PL/SQL client library for CAS (from the jasig web site IIRC), I did the following:
NB - the following is not complete, nor is the code "done". If people want a more complete solution, let me know. I just wanted to get some of this written down before I forget it.
Set up a page and marked it the "invalid session" page in the Authentication Scheme. All this page does is an immediate branch to URL from function. The function is looks like this (skipping the "declare" section for now)
The Get_Cas_Url function was lifted from the Yale code and basically does
app := v('APP_ID'); session := v('SESSION'); service := 'https://apex.cct.rpi.edu/pls/apex/Simon_Apex.cas_login?' || 'APP=' || app || '&' || 'SESSION=' || session; loginurl := get_cas_url(service); return loginurl;
The above will redirect the browser to the CAS server where we hope they complete authentication successfully (I have not tested failure cases yet). If things go well, the CAS server will redirect the session to a PL/SQL procedure that can be run by anyone (this is outside of APEX). This procedure, Simon_Apex.cas_login simply shuffles the parameters around and goes back into APEX as follows:
newsvcurl := replace(service, ':', '%3a'); newsvcurl := replace(newsvcurl, '?', '%3f'); newsvcurl := replace(newsvcurl, '&', '%26'); newsvcurl := replace(newsvcurl, '=', '%3d'); loginurl := static_login_url || '?service=' || newsvcurl;
In a more polished version I might not be using a literal for the server name, and a symbolic page name would be better, and I am not actually using the CAS_LOGIN request. This pushes you back into APEX where you hit a modified login page. The big change here is all of the PL/SQL processing is moved before the page load - we never actually display the page. The Login procedure has been changed:
procedure cas_login(app in varchar2, session in varchar2, ticket in varchar2) is apex_url varchar2(4000); begin apex_url := 'https://apex.cct.rpi.edu/pls/apex/' || 'f?p=' || App || ':' || '101:' -- Don't list a page || Session || ':' || 'CAS_LOGIN:' -- Request || ':' -- Debug || ':' -- Clear Cache || 'P101_TICKET:' -- item_names || ticket; owa_util.redirect_url(apex_url);
to pick up the Username from the Cas_Get_Username function and then "log in". There is a custom auth procedure that returns "true" - the real work was done in the Cas_Get_Username step. We then are signed on, and the normal APEX processing continues. The rest of the heavy lifting takes place in Cas_Get_Username:
begin :P101_Username := Simon.YUAPPS_CAS.cas_get_Username(:P101_TICKET); wwv_flow_custom_auth_std.login( P_UNAME => :P101_Username, P_PASSWORD => 'YY', P_SESSION_ID => v('APP_SESSION'), P_FLOW_PAGE => :APP_ID||':1' ); end;
The do_login function is from the original Yale Library and it takes the ticket (from the CAS server), and once again regenerates the service name (this should be done once and saved), and passes that pair to the cas server via the UTL_HTTP package and gets back a response that includes the authenticated username. The ticket is only good for a single use so we have some protection from replay attacks (and makes testing more annoying - can only ask once)
function Cas_Get_Username(ticket in varchar2) begin app := v('APP_ID'); session := v('SESSION'); service := 'https://apex.cct.rpi.edu/pls/apex/Simon_Apex.cas_login?' || 'APP=' || app || '&' || 'SESSION=' || session; rc := do_login (ticket=> ticket, service => service); if rc.status = SUCCESS then return upper(rc.netid); elsif rc.status = REDIRECT then trace('RC.Status=Redirect'); else trace('RC.Status = else'); end if; return Null; -- Blow out
What I have appears to work - I am certain that it can be improved - both cleaning and restructuring the code, and better integration with the APEX custom authentication. It might also be nice to avoid the first redirection page. If folks have suggestions, I would like to hear them, and if people are interested, I can perhaps clean up the code and instructions and make it available. It might be worth waiting until we actually get some APEX apps hosted in our portal and we discover what other changes are needed.
You can pretty much eliminate get_cas_url, as well as escape any other special characters with utl_url.escape:
logger@orcl> begin 2 dbms_output.put_line(utl_url.escape('https://apex.cct.rpi.edu/pls/apex/Simon_Apex.cas_login?',true)); 3 end; 4 / https%3A%2F%2Fapex.cct.rpi.edu%2Fpls%2Fapex%2FSimon_Apex.cas_login%3F PL/SQL procedure successfully completed.
[Applied Oracle Security: Developing Secure Database and Middleware Environments|http://sn.im/aos.book]