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

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.


OpenID: Trust and Liability

December 5, 2007 – 12:33 pm

My co-worker (Sam) and I lead a session at IIW called “OpenID Security and Privacy” and as the conversation evolved it occurred to me that even though these issues exist in OpenID today, the real hurdles are going to be trust and liability.

There are many companies and products doing things to improve security for OpenID. Verisign’s seat-belt plugin is excellent at thwarting phishing. Vidoop defeats keylogging and automated attacks. The sad truth is your OpenID login is already likely to be more secure than your bank’s login. There are solutions to the current security issues facing OpenID. Some of the solutions are already available; others will take some time to implement and may require your browser to be a bit smarter, but we know how to solve these problems.

There seems to be a lot of talk about trust between identity providers and relying parties. Quite a few people have suggested that there needs to be some method for a relying party to assert some level of trust with any given identity provider. Suggested methods for doing so are Idp certification or some sort of rating/reputation system. There is a lot of resistance to either of these things because it makes it hard for user’s from implementing their own Idp.

Many potential OpenID reliers have a lot of trouble with the fact that anyone can roll their own Idp. The argument is that the Rp’s shouldn’t trust every Idp out there, because evil-bad-guy can create his own Idp. This is really only true for Rp’s who don’t already have an existing relationship with the user (typically low risk transactions). Most high risk transactions between an Rp and an end user there is already an existing relationship. For example, you already have a bank account with your bank. So when you go to log into them with your OpenID it’s really not important that they trust your Idp, because the first time you logged in with that OpenID they can verify you are who you say you are via some other channel. So the real important trust relationship is between the user and the Idp. It’s important that the user select an Idp they can trust, who provides good security for their identity. The thing is user’s do this sort of thing every day. User’s decide who to trust with their identity every time they are online. They choose to trust PayPal, Amazon, eBay with their bank account information. User’s already make decisions about who to trust with their identity.

The real problem is liability. What happens when evil-bad-guy signs into Jane’s bank account with with her OpenID and steals all her money? Is the bank at fault or is the Identity provider at fault for falsely identifying the user? Companies have a really hard time buying into a technology when they don’t have someone to sue when things go wrong.


Coolest hardware authentication device ever from Yubico.

December 4, 2007 – 10:24 am

I’m not typically a big fan of hardware tokens, but I discovered a neat little device at IIW that takes the cake when it comes to hardware based authentication. The YubiCard is a incredibly small device; requires no drivers at all and doesn’t need a LCD screen. In addition to being the coolest hardware token I have ever seen it has to be one of the cheapest to produce.

The way it works is that you plug it into any USB port on your computer; then when you go to a website to login all you have to do is tab over to the password field and touch your YubiCard (No buttons, it’s touch sensitive), then the YubiCard “types” in your one-time-password for you just as if it were a keyboard device.


Android: Simple Tabs Example

November 28, 2007 – 2:11 am

It seems google may have shipped android with a tab widget that is said to be deprecated from the time of release.

Here is a simple method I’ve used to implement tab-like functionality that seems to work well for me. You can easily change the background of the buttons to look just like tabs, but I kept the example as clean and simple as possible.

Tabs Screen Shot

Full source and example project can be downloaded here


Accessing Android Resources By Name at Runtime

November 27, 2007 – 4:30 pm

Here’s a little snippet I wrote to access resources in R.java during runtime. It just uses reflection to get the filed names of the objects in the R class.

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
public class RR {
    public static Drawable getDrawable(Context context, String name) {
        Class<R.drawable> c = R.drawable.class;
        Drawable d = null;
        Field f;
        int i = 0;
 
        try {
            f = c.getField(name);
            i = f.getInt(f);
            d = context.getResources().getDrawable(i);
        } catch (Exception e) {
            Log.e("RR",e.toString());
        }
        return  d;
    }
 
    public static String getString(Context context, String name) {
        Class<R.string> c = R.string.class;
        String s = null;
        Field f;
        int i = 0;
 
        try {
            f = c.getField(name);
            i = f.getInt(f);
            s = context.getResources().getString(i);
        } catch (Exception e) {
            Log.e("RR",e.toString());
        }
        return s;
    }
 
}

With this class you could access resources at run time just like the example below. I found this extremely useful in one scenario where I switch Drawable resources out at run-time based on user interaction.

1
2
3
   // inside any view/action
   Drawable icon = RR.getDrawable(this,"myicon");
   String mystring = RR.getString(this,"icon_name");

Feel free to use the code above in your projects. I would really like to see your hacks and improvements for it.


Google Hax0rs

November 25, 2007 – 2:17 pm

I noticed this interesting entry in my server’s access log today. I certainly have phpMyAdmin running at that location, but you can’t get the process list unless you login.

66.249.70.89 xxxxxxxxx.com - [21/Nov/2007:13:43:18 -0800] “GET /MyAdmin/server_processlist.php?lang=en-utf-8&convcharset=iso-8859-1&collation_connection=utf8_unicode_ci&token=a1bb5490499a10bb493edc160625e33b&kill=49481 HTTP/1.1″ 404 345 “-” “Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)”

You can do two things at that screen:

  1. See what processes MySQL is running
  2. Stop a MySQL process

Neither of witch is something I would expect Google to be interested in on my personal webserver.