Updating Page Sentry for APEX 4.0 Upgrade

2010-12-09 19:00:00 -0500


We’ve been using Oracle’s Application Express 3.0.x on a client site for about three years now, possibly more. Last week we did the upgrade to Application Express 4.0.2 on a development host, and for the most part it was seamless and our apps worked great. In addition, the new Application Express engine is snappier, easier to work with, and really quite fantastic.

However, we did run into some small troubles that became quite vexing. We use APEX in a single-sign-on environment (Oracle Access Manager is involved), so instead of using APEX’s built-in authentication schemes, we use a custom one we call oamPageSentry, based off of the many examples out there on doing just that (there’s even a kind of unofficial white-paper from Joel Kallman, one of Oracle’s APEX hackers, on creating a “page sentry” authentication scheme in PL/SQL for OAM over here on his blog).

For the sake of brevity, if you’d like to peak at our original page sentry function at the time of the upgrade, in it’s resplendent, mod_plsql glory, check out this gist.

Anyway, the strange behavior we noticed had to do with links used to forward users already authenticated in our SSO environment into a specification application and page in APEX, setting certain required page items in the reports using values passed in through the APEX URL request format. After the upgrade, we saw was that the user was getting a redirect that squashed the Page Item parameters by sending the user to a new session every time, without the original query string preserved.

This seems due not to anything wrong with the original page sentry code (ours or Oracle’s, which were identical in most aspects), but with some slight changes in behavior in APEX to avoid session manipulation, although it was quite tricky to narrow it down.

The primary problem seems to be that when the user visits a session-id-zero link, APEX 4.0 redirects the user to a new version of the URL with a new session ID every time, even if the user already has a session. We found this quite accidentally; we created a simple test app using built-in authentication to narrow things down, and we decided on a lark to include the display of the &APP_SESSION. variable. In other words, for a URL like this:

http://dev.example.com:7777/pls/apex/f?p=106:1:0::YES::P1_FOO:barbar

We’d expect to end up at something like:

http://dev.example.com:7777/pls/apex/f?p=106:1:3343328572473537::YES::P1_FOO:barbar

And on every subsequent request, we should keep landing at that same link with the same session ID (the value 3343328572473537 in the example above). However, that’s not what was happening, we were getting a new session ID every time. Even more curiously, the value being displayed from APP_SESSION never changed, it was always left at the value of the first session ID we were given in that browser session.

Another problem that was happening was that page items we passed over the original session-id-zero link were indeed retained in the redirect URL, but were not used to set the values in session, despite session protection being off. We think this is because APEX won’t properly manipulate the session from url parameters unless the session IDs agree, and in this case, they wouldn’t.

Now, this was all without the page sentry authentication turned on, so we finally had a culprit — this particular behavior would cause the page sentry function to behave somewhat incorrectly. In our page sentry, which attempts to emulate what APEX normally does, for users that already have a cookie, it just registers the existent session and attempts to forward you to the page you originally requested. Except the session IDs no longer match (because a new session has been generated by APEX thanks to session ID zero), so the URL the user is sent to is for a new session, but the parameters/page item values have been stored in the original session and ignored in the new one.

At this point we decided to adapt the more recent page sentry function listed in Joel Kallman’s blog post as our starting point for getting this working correctly, since his seemed more comprehensive and careful of edge-case scenarios. It looks like this:


create or replace function PASSPORT.oamPageSentry ( p_apex_user in varchar2 default 'APEX_PUBLIC_USER' )
return boolean
as
l_cgi_var_name varchar2(100) := 'REMOTE_USER';
l_authenticated_username varchar2(256) := upper(owa_util.get_cgi_env(l_cgi_var_name));
--
l_current_sid number;
begin
-- check to ensure that we are running as the correct database user
if user != upper(p_apex_user) then
return false;
end if;

if l_authenticated_username is null then
return false;
end if;


l_current_sid := apex_custom_auth.get_session_id_from_cookie;
if apex_custom_auth.is_session_valid then
apex_application.g_instance := l_current_sid;
if l_authenticated_username = apex_custom_auth.get_username then
apex_custom_auth.define_user_session(
p_user=>l_authenticated_username,
p_session_id=>l_current_sid);
return true;
else -- username mismatch. unset the session cookie and redirect back here to take other branch
apex_custom_auth.logout(
p_this_app=>v('APP_ID'),
p_next_app_page_sess=>v('APP_ID')||':'||nvl(v('APP_PAGE_ID'),0)||':'||l_current_sid);
apex_application.g_unrecoverable_error := true; -- tell apex engine to quit
return false;
end if;

else -- application session cookie not valid; we need a new apex session
apex_custom_auth.define_user_session(
p_user=>l_authenticated_username,
p_session_id=>apex_custom_auth.get_next_session_id);
apex_application.g_unrecoverable_error := true; -- tell apex engine to quit
--
if owa_util.get_cgi_env('REQUEST_METHOD') = 'GET' then
wwv_flow_custom_auth.remember_deep_link(p_url => 'f?'|| wwv_flow_utilities.url_decode2(owa_util.get_cgi_env('QUERY_STRING')));
else
wwv_flow_custom_auth.remember_deep_link(p_url=>'f?p='||
to_char(apex_application.g_flow_id)||':'||
to_char(nvl(apex_application.g_flow_step_id,0))||':'||
to_char(apex_application.g_instance));
end if;
-- -- register session in APEX sessions table,set cookie,redirect back
apex_custom_auth.post_login(
p_uname => l_authenticated_username,
p_session_id => nv('APP_SESSION'),
p_app_page => apex_application.g_flow_id||':'||nvl(apex_application.g_flow_step_id,0));
return false;
end if;
end oamPageSentry;
/

The first thing we needed to do with the above listing is put in a check for the session ID mis-match caused by linking the user to a page with session zero. If we have the “current”, or originally created session ID in the user’s cookie, we can check to see whether or not it matches what is in the QUERY_STRING, and if they don’t match, redirect the user to the very same URL but with the “current” session ID:


l_current_sid := apex_custom_auth.get_session_id_from_cookie;
l_url := wwv_flow_utilities.url_decode2(owa_util.get_cgi_env('QUERY_STRING'));

-- split on zero or more non-colon characters
l_url_sid := REGEXP_SUBSTR(l_url, '[^:]*', 1, 5);

-- does the current sid match the sid in the url?
if owa_util.get_cgi_env('REQUEST_METHOD') = 'GET' AND l_current_sid <> TO_NUMBER(l_url_sid) then
-- nope, so let's go to the correct url, with the current sid
wwv_flow.debug('oldurl: ' || l_url);
l_url := REGEXP_REPLACE(l_url, '^(p=.+?:.+?):*\d*(.*)$', '\1:' || l_current_sid || '\2');
wwv_flow.debug('newurl: ' || l_url);
owa_util.redirect_url('f?'|| l_url);
return false;
end if;

Now, if the user comes in on session 0 from a link, and they already have a session, but have been redirected to a new/different session ID, page sentry fixes it and redirects the user to the appropriate URL.

However, there are other problematic edge cases. Most Stephen took care of with the clever regex matching used above to identify the session ID if present in the QUERY_STRING, but the real bugger was post_login. In previous versions of APEX, we believe that the remember_deep_link call would cause any subsequent call to post_login to redirect to the user to the target URL. This doesnt appear to be the case in APEX 4.0.

To account for this, we updated the call to pass the target page in to the post_login call directly. We found that post_login will blindly append the session ID to the end of p_app_page parameter string when it redirects, and we can clean that up with the another cleanup-redirect at the beginning of our page sentry function:


-- the post_login call at the end of this function will blindly append the session ID to the URL, even if it is
-- a deep link. Detect this condition, strip the duplicate session identifier, and redirect.
if REGEXP_SUBSTR(l_url, '^.*:' || l_current_sid || ':.+:' || l_current_sid || '$') IS NOT NULL then
l_url := REGEXP_REPLACE(l_url, ':' || l_current_sid || '$', '');
wwv_flow.debug('oamPageSentry: identified duplicate session id on URL, stripping and redirecting to ' || l_url);
owa_util.redirect_url('f?'|| l_url);
return false;
end if;

This set of fixes has gotten us through, straightening out all the redirecting and link remembering that needs to be done when our users come in to link in our pages with params they need and session ID 0.

TL;DR

You can grab the full PL/SQL for our implementation of this page sentry function at this gist.


Codebook 1.4.1 Released in App Store

2010-12-01 19:00:00 -0500


As previously noted, we pulled Codebook 1.4.0 from the App Store due to a start-up bug, and rushed to get a fix prepared. The update has been released, Codebook 1.4.1 is now available in the App Store, and all users of Codebook should update as soon as possible. Visit the App Store now to get the update.

If you had installed Codebook 1.4.0, or any other recent update and could no longer start Codebook on your device, this update will restore your access to your data. As we mentioned before, we had done quite extensive testing on Codebook before releasing the last update, but we didn’t do it on enough disparate iOS devices to detect this bug, and will be doing far more extensive testing on all future updates.

Changes

Well, obviously, we fixed the start-up bug.

But the biggest change since Codebook 1.3 is that Codebook 1.4 provides the oft-requested support for multi-tasking: on devices that are running iOS 4 and support multi-tasking, you can now set a timer, allowing Codebook to stay temporarily unlocked while you switch applications. Makes the app a lot more convenient to use on the Subway, I’ll tell you that.

I’ve also ditched the recycled-paper background displayed on the note view/edit screen in favor of a more subtle paper texture that’s far more pleasant.

Coming Soon in Codebook 1.5

Work on Codebook 1.5 was completed recently, and we’re now testing it as mentioned above for any surprises. It includes a ton of usability improvements and a fantastic new feature: Dropbox Sync.

Codebook 1.5 Sync View

The note view/edit screen also got a lot of love, too:

Codebook 1.5 Note View

If the differences aren’t readily apparent: the top-right navigation bar button is no longer the action button kicking up an email compose view. This + button allows you to quickly add a new note — the current note is torn off the view (a la Apple’s MobileNotes) to reveal a fresh page.

The toolbar is a long-overdue addition. Now you can delete what you’re looking at without having to pop back to the list view, or you can hit the Action/Share button. The share button, by the way, no longer just throws up a compose view. Instead it presents a modal sheet providing you the option of sharing the current note as an email. This pause in the process allows us to add more options later and will help cut down on some accidental mailing of notes.

Finally, while you can’t tell from the screenshot here, the animation of presenting the keyboard and re-sizing the text view for editing is greatly improved. It’s no longer jerky, but nice and smooth, and the background doesn’t appear to get squished or stretched, it just remains static, providing a much nicer editing experience.

This is all probably a couple weeks out from appearing in the Store. Hopefully, by Christmas.

Thanks for using Codebook!


STRIP: Not Cheap And Nasty

2010-11-30 19:00:00 -0500


SY, a fan of our password manager STRIP for iOS sent us one of the funnier complements we’ve ever gotten:

Overall I love the app, the interface is clean and simple but doesn’t look cheap and nasty like XXXX and others.

Thanks, SY!


Codebook Startup Bug in 1.4.0

2010-11-16 19:00:00 -0500


Updated, Update II, below

Bomb Restart gif

Thanks to direct reports from our customers, and some crash logs we’ve received via iTunes Connect, it appears that the new version of Codebook that we published in the App Store (version 1.4.0) had a start-up bug in certain circumstances, on certain iOS versions and hardware. Basically, the app was crashing immediately on iOS 3, and crashing on some instances of iOS 4 during the application start-up sequence. We’ve since pulled Codebook from the store to prevent anyone else from downloading this update.

No one’s data is lost, but anybody who’s run into this problem will find their data inaccessible, simply because they can’t start the application. To that regard, we are hustling to deliver a fix through the App Store (we’ve already got the bugs solved, and thoroughly tested). We realize that if you’re a serious user of Codebook, you depend on being able to access your data, and we’re on it.

For all future releases of Codebook, we’ll be testing on more devices, iOS 3 and 4, to make sure this doesn’t bite us again. I should note that we had done extensive testing of Codebook, but it was limited to iOS 4 on an iPhone 4, and iOS 4 on an iPhone 3G. Clearly, we need to be testing this on the iPod Touch, on iOS 3, etc, and that’s what we’ll be doing from now on.

We do have some good news: Codebook 1.5 is already undergoing testing and should be ready for release soon. The main reason for the version bump, aside from a ton of user interface and usability improvements, is the new feature: Sync with Dropbox!

Thanks again for your patience and feedback.

Update, Nov 23rd, Codebook 1.4.1, which fixes the issues outlined here, was submitted to Apple on Sunday (Nov 21) with a request for expedited approval due to the situation at hand. We’re waiting to hear back, hopefully it will be approved and posted to the App Store within the week.

Update II, Dec 2nd, Codebook 1.4.1 is now available from the iTunes App Store. All users of Codebook should upgrade as soon as possible. Release notes (and sneak peaks).


Strip Desktop and the Future of Strip

2010-10-28 20:00:00 -0400

Updated: 6/6/2012 Strip for Windows and Strip for OS X are now for sale.

It’s about time we came out and said it: we are in fact working on Strip Desktop. Long requested by fans of our popular private data management app for iOS (and previously for Palm OS), Strip Desktop will provide a means for users to manage their Strip data on their desktop computer, in addition to the features we’ve already made available in Strip Sync.

Strip MacOSx Promo 500w

As you can hopefully tell from the screen clip above (click to enlarge), we are working hard to maintain the same simple and elegant style used in Strip for iOS, by borrowing some of our design choices from Apple’s Address Book. We expect this interface to change and mature quite a bit over the next few months, but this is a reasonable approximation of our intended product. You can’t tell from that lone screenshot, but all the basic features of the data editor in Strip for Mac OS X are complete at this point.

Strip Desktop is a bit of a misnomer, as it actually describes two distinct, native applications: Strip for Windows, and Strip for Mac OS X. We’ve been making great progress recently on the Mac version, but we’ve fallen a bit behind on the Windows version and expect to pick up the slack soon. We are considering publishing Strip for Mac OS X in the new Mac App Store recently announced by Apple, and expect to have it ready for release by the time the Mac App Store opens for business, whether or not we decide to go that route (Apple estimates this will be around Jan 26th, 2011, so that’s our target).

Online Backup & Strip

We’ve reconsidered our previous stance on providing an online-backup feature. We’ve found that supporting Sync over local WiFi networks is particularly difficult. With so many different types of home networking hardware out there — with all the infinite variations of network configurations, combinations, pitfalls, and firewalls — it’s no wonder Apple does sync over the Dock cable. It’s time for us to find ways to make this easier, too.

We’re not getting rid of Sync over WiFi, but we are going to begin experimenting with an online backup & sync feature for Codebook, and if all goes well, we’ll port it back to Strip. The basic gist of the feature is that a copy of your encrypted db will be placed in your Dropbox account. When it’s time to sync, any one of your copies of Strip can download the master copy in your Dropbox, and sync against it locally. Your unencrypted data will never be stored on Dropbox, and your password/key will never be sent over the wire. This feature will have the happy side-effect of providing you with the same multi-device replication you get now with Strip Sync.

Discontinuing Strip Sync

Relatedly, we have one maintenance update in the pipe for Strip Sync, and we plan to discontinue development of the application once Strip Desktop is released. Once we have Strip Desktop and the Online Backup feature in place, there won’t be a need for the software anymore — whether or not you choose to use Strip Desktop. The forthcoming maintenance update of Strip Sync will coincide with the release of Strip 1.5 for iOS, a rather handy set of small productivity enhancements and bug fixes. Users who upgrade to Strip 1.5 will need the latest version of Strip Sync, as well. We’ll publish a notice to the mailing list as soon as this next release is ready.

Android

We really appreciate the requests you’ve been sending in for an Android port of Strip! At the moment we need to focus on the desktop applications. Once we’ve brought them to market, we will take another look at building a port for Android.