Steven Osborn
"I would love to change the world, but they won't give me the source code".

OpenID Personas = Cruft

March 20, 2008 – 12:30 pm

I use personas in the same way I think most people are using them in OpenID today. I don’t think of them as identity containers, but as address bundles. I have one very creatively labeled “Work” and another labeled “Home”. Hmm…. That sounds exactly like how I manage my identity in my address book except now the UI is more complex and spread out all over the place.

It seems to me that a good deal of OpenID providers today have translated multiple user accounts and/or online identities into OpenID “personas”. Which is essentially an attempt at replicating our mental picture of the semantic web. For users the web consists of many user-names for many websites; the correlation is typically one to one. The OpenID model draws a different picture where your authentication is now a one to many relationship which is why these methodologies don’t mesh well.

The point is with very few exceptions (mental case) we are all individuals with one personality or persona who happen to have multiple sets of data that describe us. The concept of personas only serves to confuse users in a failing attempt to replicate previous models.

This is how I want to manage my identity online: Easy and familiar.

profile.jpg


I blog therefore I am.

March 15, 2008 – 1:16 am

I am now the official Steven Osborn of the internet. How did I accomplish the feat? Well, it wasn’t by spending hours practicing to be an accomplished pianist or by becoming an experienced realtor. I certainly didn’t take the time to become a doctor and I’m not much of an athlete.

What I have done is discovered the dark art of blogging. In this society where the success or failure of an individual or business is measured by their Google PageRank™; bloggers rule the world. We now live in a time where any slightly technical opinionated idiot can make a name for themselves and become blogfamous. Due to a small glitch in social history Geeks have managed to become social icons and have created numerous virtual hangouts where they can accumulate virtual friends, participate in virtual games and do virtual chores all the while avoiding actual human contact.

So what will I do now that I’ve reached the ePeen level cap? Try to take over the world!

Notice: This message may or may not self destruct in 30 years dot com.


iPhone SDK Sucks - Why Apple doesn’t have a clue

March 8, 2008 – 10:35 pm

If you didn’t run out and upgrade to Leopard and/or buy a spanking new Intel Macintosh, you won’t be developing applications for the iPhone. Besides, unless you’ve done a bit of Mac development before, your going to be behind because iPhone applications are written in Objective C. Who really wants to learn Objective C just to hack on their iPhone? Not me.

To make matters worse, once you’ve finished developing your beautiful new iPhone application the only way you’ll be able to distribute it is through iTunes, and only after Apple takes a %30 cut.

Can you image what would happen if Microsoft said they were releasing a new version of Windows mobile that could only be developed on from Windows Vista on a nearly new PC and when you were done you could only distribute applications for it through Windows Media and they were going to take %30 of your profits!? Everyone would just laugh at them all the way to court.

Guess it’s back to hacking on broken bits of Android… *sigh*


iPhone SDK is as Open as Google’s Android

March 7, 2008 – 12:13 am

Although Android touts it’s platforms openness; It is really no more open than any other mobile platform on the market as of now. You cannot download the source code for any of their components. What does this mean? Well, basically that they have YAMOS (Yet Another Mobile Operating System) that is less stable and complete as their competitors and nothing more. You may be able to run Android on dozens of devices in the near future, but is it going to be to late? Today you can test Android on exactly zero devices and in the meantime millions of iPhones are being sold. If iPhones becomes the iPod of their market, then the best Android can hope for is to eat the crumbs from it’s table.

So as a developer with a little spare time I look at both iPhone and Android and see:

  • Open API’s
  • Closed (or hidden) Source
  • Retarded amounts of money within reach.

So the only real factor left IMO is stability and sexiness of the platform and iPhone is the clear winner at this point.

Most consumers could care less if their mobile phone is running on an open platform, but developers care a lot. So while Google is watching their stock price rise out of control marketing projects like “Open Widgets” and Android as open platforms, developers like me are asking “Where’s the code!” It seems like “Open” has been reduced to another buzz word that’s meaningless 2.0.

Both Android and iPhone are attempting to throw copious amounts of money at developers to motivate them to develop for their platform, but I wonder if they realize that developers would just appear if they put their code where their Benjamins are and give developers something they can mold. I’m pretty sure the Android team never actually had the Bazaar model in mind, but why not?

Sure, Money is a great motivator, but so is code and code has a nice side affect of promoting collaboration and communication where money does not.


Distributed Versioning - The new wheel

February 15, 2008 – 10:50 am

With all of the great distributed versioning tools out there it’s a wonder everyone is still using traditional client-server versioning systems like SVN or CVS. Especially when you consider how trivial distributed systems are to setup for small projects. Unless your hosting your SVN project with some one like Sourceforge, LaunchPad or Google Code, it is much easier with a system that doesn’t require you to setup a central repository. I know… “If it ain’t broke don’t fix it.”, but sometimes you don’t realize how broke something is until you’ve used something that’s… less broke.

I’ve spent some time looking at several open-source distributed SCMs and the most prominent one’s from my point of view are the following:

Each of the above SCMs are production ready distributed systems and have notable projects under their belts, but each one has properties that suit certain projects better than others, so I recommend doing some reasearch on your own because what works for me may not meet your needs.

I typically work in a unix like environment from the command line, but I also collaborate with people who do not, therefore I tend to shy away from solutions that don’t function well on all platforms. For me this eliminated Darcs and Git from the start. Although both of the aforementioned SCM systems are feature rich and technically strong, their windows support is pretty dismal compared to the other competitors on the list.

So for me the most appealing options are Bazaar (bzr) and Mercurial (hg). Both of these projects have strikingly similar offerings. The both have clients/plugins for Eclipse, Windows Explorer (Tortise__), and Trac. Both systems are written in Python and have a dependency on the Python libraries, but it is relatively easy to find binary installers for Python, Bazaar and Mercurial so if Python isn’t your thing, (Hard to imagine) it doesn’t really matter. Bazaar is used heavily by the company behind Ubuntu (Canonical) who is also responsible for Launchpad. Mercurial is used by several big-name projects such as Mozilla and OpenJDK. I’ve used both Bazaar and Mercurial for some small projects and for me Mercurial really stood out.

For anyone looking for a distributed SVN replacement, I definitely recommend giving Mercurial a shot. Since they’ve updated their logo there’s no reason not to! Seriously though, Mercurial was an extremely painless transition for me. The user interface was extremely familiar and intuitive, has clients available for just about everything and was extremely painless to install and use. The fact that it just felt familiar coming from SVN to me and it is noticeably faster for most tasks than Bazaar put it at the top of my list.


Miami Here I come

February 14, 2008 – 11:54 pm

I’ll be in Miami along with Kevin Fox, also from Vidoop, toward the end of the month for a couple of different events that should be a lot of fun. I’ll be catching BarCamp on the 28th followed by
FOWA - Future of Web Apps. I can’t wait to geek out at all the events and see Miami.


Framework Envy

February 3, 2008 – 1:15 pm

I’m typically a PHP guy. I wrote my first web page using PHP3 and have created some pretty robust websites since then and for the most part it’s been a pleasure to work with. I also have a bit of experience using Zend Framework which among other things has a really strong MVC implementation, but I can’t help but feel a bit left behind when I look at features of some other modern frameworks. I’ve been playing around with Django for a few days and a few things have occurred to me.

Validation logic should be part of the model

Take a look at this bit of code from Django.

1
2
3
4
5
6
# in visitor/models.py
class Visitor(models.Model):
    name = models.CharField(maxlength=30)
    email = models.EmailField(maxlength=200,blank=true)
    website = models.URLField()
    donated = models.DecimalField(max_digits=5, decimal_places=2, blank=true)

In the Zend_Validator is much more complex, typically happens in the controller and has no direct relationship with the data model your validating for.

1
2
3
4
5
6
7
8
9
10
11
12
13
$nameValidator = new Zend_Validate_StringLength(1,30);
$emailValidator = new Zend_Validate();
$emailValidator->addValidator(new Zend_Validate_StringLength(0, 200))
               ->addValidator(new Zend_Validate_EmailAddress());
 
// Zend doesn't have a URL validation class, so we would have to 
// create a custom validator that extends Zend_Validate
$website = new Custom_Validator(); 
 
// This only validates that the number is a floating point number, 
// in reality we would probably have to write another custom validator
// to validate monetary values.
$donated = new Zend_Validate_Float();

The nice thing about Django is the actual database tables are created from the model. So when you declare a name field with the maxlength of 30, your validation rules knows your fields max length because it’s based from the same one line of code the database field was created from! I’m not sure I believe building database tables from your model code is the only way to do things, but I am sure complex validation logic for models belongs in their implementation, not in the controller.

Template inheritance rocks.

This is sadly something I had never seen before. Instead of having half a dozen include statements in each of your views you can just have on that says that it extends your base template. The nice thing about this is the child view/template can override what ever portions of the base template it wants, or it can just go with what the parent has. Django’s documentation has a really good example of template inheritance in action.

An auto generated admin interface is more valuable than gold.

Before playing with Django I watched a few screen casts to get a feel for what it was all about. Then Django seemed like a one trick pony to me and the one trick seemed a bit lame. I mean, auto generated administration interface… who cares. Since then I’ve learned Django certainly has more than one trick up it’s sleeve and their main act is really beautiful in person. It is honestly as robust (or more so) as any administration interface I’ve built in the past. I expected something clunky and extremely _not_ user friendly. I really expected it to generate an admin interface that made it somewhat more convenient for programmers, but wouldn’t work for the average Joe. Well, when they say it’s “production ready” that’s exactly what they mean. The number of hours this saves (that can be spent developing fun stuff) is extremely valuable.

Admin scripts

I know this will sound a bit lazy, but I don’t like having to manually create half a dozen empty folders every time I set up a new project, controller etc. Rails and Django both do a good job of generating framework skeletons for you so you can plug away without missing a beat.

Shell to poke at objects

This is really a language feature and not a framework feature, but you can do some really sexy stuff inside the Django shell like insert a new visitor:

>>> p = Visitor(name="Steven", website="http://steven.bitsetters.com")
>>> p.save()

Or select the last five blog posts

>>> recent = BlogPosts.all()[:5]

This makes it possible to stay in Python land forever if you wish. No bouncing around between SQL and Python.

Built-in development servers make getting started fun

There is nothing like spending half an hour setting up Apache and mod_* before writing your first line of code. Django and Rails bundle a lightweight web server who’s sole purpose is to make development easy and convenient. Unfortunately you won’t be seeing a bundled PHP based web server with your favorite PHP framework anytime soon. Try writing one and see how far you get without things like thread support. PHP is really more of a template language itself than a general purpose programming language. Projects like PHP-GTK make me giggle. Single threaded GUI applications are awsome! </sarcasm>


Using JanRain OpenID with Zend Framework

January 28, 2008 – 4:35 pm

Ok, I know Zend_OpenId is on the way, but for those of us who don’t like to wait here’s a way to get OpenID working with Zend Framework. Besides, JanRain’s libraries tend to be pretty solid and up to date so you won’t go wrong using this in place of Zend_OpenId if you choose to in the future.

This implementation is pretty simple. They only thing thrown in is support for saving the last page the user visited on your site so you can return them there once authentication is finished.

I didn’t include any extension support for the simplicity sake, but if you wanted to add Simple Registration or something the examples that come with the JanRain libraries are fairly straightforward.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
<?php
 
// Make sure these files from the JainRain libraries
// are in your path.
require_once "Auth/OpenID/Consumer.php";
require_once "Auth/OpenID/FileStore.php";
require_once "Auth/OpenID/SReg.php";
 
/**
 * OpenID login controller using JanRain OpenID 2.0  
 * libraries.
 *
 * By: Steven Osborn - http://steven.bitsetters.com
 */                    
class LoginController extends Zend_Controller_Action {
 
    protected $consumer = null;
    protected $store = null;
    protected $domain = 'example.com';
    protected $session = null;
    protected $openid = null;
 
    /**
     * Setup some resources we'll need later for storing 
     * and retrieving data.
     */           
    public function init() {
        $this->session = Zend_Registry::getInstance()->get('session');
        $this->store = $this->getFileStore();
        $this->consumer = new Auth_OpenID_Consumer($this->store);
    }
 
    /**
     * Create a file store object for consumer
     */
    public function getFileStore() {
        $store_path = "/tmp/openid";
 
        if(!file_exists($store_path) && !mkdir($store_path) ) {
            echo "Could not create file store.";
            exit;
        }
 
        return new Auth_OpenId_FileStore($store_path);
    }
 
    /**
     * This is the default action for the controller. Which
     * does two basic things.  
     * 1. Records what page the user came from so we can return
     *    them there after login.
     * 2. Renders the index view. (Login form).  Since this is the
     *    index action it will automatically render 
     *    views/scripts/login/index.phtml without explicitly calling
     *    $this->view->render('index');
     */
    public function indexAction() {
 
        // Save the page the user was last at and return them to it 
        // after authentication.
        $return_url = parse_url($_SERVER['HTTP_REFERER']);
        if($return_url['host'] == $this->domain) {
                $return = $return_url['path'];
                if(strlen($return_url['query'])) {
                    $return .= "?" . $return_url['query'];
                }
                if(strlen($return_url['fragment'])>0) {
                    $return .= "#" . $return_url['fragment'];
                }
                $this->session->loginReturn = $return;
        }
    }
 
   /**
    * The login view from the index action posts to this action, then
    * we call consumer->begin() whichs starts the openid login process
    * with the open id from the form post.
    */
    public function tryAction() {
        try {
            // Make sure the user entered something.
            if(strlen($this->openid) == 0) {
                throw new Exception('OpenID is empty.');
            }
 
            // Try to start an openid authentication.
            $auth_request = $this->consumer->begin($this->openid);
 
            if(!$auth_request) {
                throw new Exception("No Auth Request");
            }
 
           $redirect_url = $auth_request
              ->RedirectURL($this->getTrustRoot(),$this->getReturnTo());
 
            if (Auth_OpenID::isFailure($redirect_url)) {
                throw new Exception("Could not redirect to server: "
                  . $redirect_url->message);
            }
 
        } catch (Exception $e) {
            $this->view->error = $e->getMessage();
            $this->render('index');
            return;
        }
 
        header("Location: ".$redirect_url);
        $this->render('index');
    }
 
    /**
     * When the identity provider is done having their
     * moment with the user they get returned to this action.
     * Here we look at the response status codes to decide
     * to if they were authenticated or not.
     */
    public function finishAction() {
            $return_to = $this->getReturnTo();
            $response = $this->consumer->complete($return_to);
 
            if($response->status == Auth_OpenID_CANCEL) {
                $this->view->error = 'Verification cancelled.';
            } else if ($response->status == Auth_OpenID_FAILURE) {
                $this->view->error = "OpenID authentication failed: "
                  . $response->message;
            } else if ($response->status == Auth_OpenID_SUCCESS) {
                $this->view->error =  "Success";
                if(isset($this->session->loginReturn) &&
                   strlen($this->session->loginReturn) > 0) {
                    $user = new User($response->identity_url);
                    if($user->isEnrolled()) {
 
                        // TODO:  Vodoo for setup up a user session and
                        // logging them in.
 
                        // If the user is enrolled, return them to the page 
                        // they visited before the
                        header("Location: " . $this->session->loginReturn);
                        exit;
                    }
                    // The user was logged in successfully, but this is 
                    // their first time on your site, so send them through
                    // enrollment.
                    header("Location: /enroll");
                }
            }
        $this->render('index');
    }
 
    //////////////////////////////////////
    //  HELPER FUNCTIONS
    /////////////////////////////////////
 
    /**
     * Returns http or https depending on the current protocol
     */
    private function getScheme() {
        $scheme = 'http';
        if (isset($_SERVER['HTTPS']) and $_SERVER['HTTPS'] == 'on') {
            $scheme .= 's';
        }
        return $scheme;
    }
 
    /**
     * Answers the URL we wish for the identity provider to return
     * a user to once the authentication process is finished.
     */
    private function getReturnTo() {
        return sprintf("%s://%s/login/finish",
                       $this->getScheme(), $_SERVER['SERVER_NAME']);
    }
 
    /**
     * Answers the trust root for this domain.
     */
    private function getTrustRoot() {
        return sprintf("%s://%s/", $this->getScheme(),
         $_SERVER['SERVER_NAME']);
    }
}
 
?>

What everyone’s Zend_View_Helper tutorials leave out.

January 27, 2008 – 4:00 pm

There seems to be several Zend_View_Helper tutorials out there and all of them talk about what a view helper is and does, but I always left with one question: How do I make the helpers available to ALL of my views?

Then I learned a bit about Zend_Controller_Action_HelperBroker

This class can work all kinds of magic, but the one thing we care about right now is that it will allow you to register a global View object to be passed to your Controller script. So all you have to do is create your own View and use addHelperPath() to tell it where your View helpers are and register your view with Controler_Action_HelperBroker

Just add the following bit of code to your front controller:

//Setup view helpers
Zend_Loader::loadClass('Zend_View');
$view = new Zend_View();
$helper_path = "/var/www/myhelpers";
$view->addHelperPath($helper_path,'View_Helper');
Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer')
  ->setView($view);

Wacky bugs

December 18, 2007 – 11:59 am

Mac User’s BEWARE:
This one is probably one of the nastiest bugs I’ve seen in a while:


http://www.tuaw.com/2007/12/16/quickbooks-users-be-cautious-of-recent-update/

I know someone personally who was bitten by this one. Upgrading Quickbooks will actually delete your entire Desktop folder and all of it’s contents. There are a couple of suggested work-arounds people have posted. Intuit really let this one slip through.

Apparently the guys at Microsoft work really hard. In fact, in order to have more productive months, they have extended them to 49.7 days. Here is some evidence that it must be so:

MSSQL

http://support.microsoft.com/kb/930484

Internet Explorer 6

http://support.microsoft.com/kb/904161

Windows 95/98

http://support.microsoft.com/kb/q216641/

Actually it turns out there are exactly 4,294,967,296 ( The size of a DWORD ) seconds in 49.7 days. Which seems to be the common thread in each of these bugs. It appears running windows update should have fixed all of these issues some time ago, but as a software developer I can always appreciate a neat bug.