<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">

<head>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=us-ascii">
<meta name=Generator content="Microsoft Word 12 (filtered medium)">
<style>
<!--
 /* Font Definitions */
 @font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
        {font-family:Tahoma;
        panose-1:2 11 6 4 3 5 4 4 2 4;}
 /* Style Definitions */
 p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0in;
        margin-bottom:.0001pt;
        font-size:11.0pt;
        font-family:"Calibri","sans-serif";}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
        {mso-style-priority:99;
        color:purple;
        text-decoration:underline;}
p.MsoListParagraph, li.MsoListParagraph, div.MsoListParagraph
        {mso-style-priority:34;
        margin-top:0in;
        margin-right:0in;
        margin-bottom:0in;
        margin-left:.5in;
        margin-bottom:.0001pt;
        font-size:11.0pt;
        font-family:"Calibri","sans-serif";}
span.EmailStyle18
        {mso-style-type:personal;
        font-family:"Calibri","sans-serif";
        color:windowtext;}
span.EmailStyle19
        {mso-style-type:personal-reply;
        font-family:"Calibri","sans-serif";
        color:#1F497D;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-size:10.0pt;}
@page Section1
        {size:8.5in 11.0in;
        margin:1.0in 1.0in 1.0in 1.0in;}
div.Section1
        {page:Section1;}
 /* List Definitions */
 @list l0
        {mso-list-id:401030078;
        mso-list-type:hybrid;
        mso-list-template-ids:2138605812 1636065908 67698691 67698693 67698689 67698691 67698693 67698689 67698691 67698693;}
@list l0:level1
        {mso-level-start-at:0;
        mso-level-number-format:bullet;
        mso-level-text:\F0B7;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;
        font-family:Symbol;
        mso-fareast-font-family:Calibri;
        mso-bidi-font-family:"Times New Roman";}
@list l0:level2
        {mso-level-number-format:bullet;
        mso-level-text:o;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;
        font-family:"Courier New";}
@list l0:level3
        {mso-level-tab-stop:1.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l0:level4
        {mso-level-tab-stop:2.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l0:level5
        {mso-level-tab-stop:2.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l0:level6
        {mso-level-tab-stop:3.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l0:level7
        {mso-level-tab-stop:3.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l0:level8
        {mso-level-tab-stop:4.0in;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l0:level9
        {mso-level-tab-stop:4.5in;
        mso-level-number-position:left;
        text-indent:-.25in;}
ol
        {margin-bottom:0in;}
ul
        {margin-bottom:0in;}
-->
</style>
<!--[if gte mso 9]><xml>
 <o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
 <o:shapelayout v:ext="edit">
  <o:idmap v:ext="edit" data="1" />
 </o:shapelayout></xml><![endif]-->
</head>

<body lang=EN-US link=blue vlink=purple>

<div class=Section1>

<p class=MsoNormal><span style='color:#1F497D'>I thought of another solution to
this issue this morning in the shower.&nbsp; Rather than creating an additional master
can_access() method in CurrentUser, this could be handled with model hooks.
JDBI uses hooks to perform processing before_create, after_create, etc. We
could add an additional set of hooks called to do access checking, such as
before_access() (and/or before_read, before_write, etc.).<o:p></o:p></span></p>

<p class=MsoNormal><span style='color:#1F497D'><o:p>&nbsp;</o:p></span></p>

<p class=MsoNormal><span style='color:#1F497D'>This would allow the user of a
class to arbitrarily associate additional checks for access. The weakness I see
immediately though is if the system should be operated as an AND or an OR or if
there could be a way to give it enough power to do both. That is, if multiple
hooks are assigned, do all have to return true in order to gain access or does
just one have to return true?<o:p></o:p></span></p>

<p class=MsoNormal><span style='color:#1F497D'><o:p>&nbsp;</o:p></span></p>

<p class=MsoNormal><span style='color:#1F497D'>Other than answering that question,
the implementation should be relatively simple and provide additional power in
a very straightforward manner.<o:p></o:p></span></p>

<p class=MsoNormal><span style='color:#1F497D'><o:p>&nbsp;</o:p></span></p>

<p class=MsoNormal><span style='color:#1F497D'>I still like the idea of having
the can_access() mechanism on CurrentUser, but I don&#8217;t know how anyone else
feels about it. It would actually be possible to have both the can_access()
method and the before_access() hooks, but that might just be over-complicating
things.<o:p></o:p></span></p>

<p class=MsoNormal><span style='color:#1F497D'><o:p>&nbsp;</o:p></span></p>

<p class=MsoNormal><span style='color:#1F497D'><o:p>&nbsp;</o:p></span></p>

<div>

<p class=MsoNormal><b><span style='font-size:10.0pt;color:#1F497D'>--</span></b><span
style='font-size:12.0pt;font-family:"Times New Roman","serif";color:#1F497D'><o:p></o:p></span></p>

<p class=MsoNormal><b><span style='font-size:10.0pt;color:#1F497D'>Andrew Sterling
Hanenkamp</span></b><span style='font-size:12.0pt;font-family:"Times New Roman","serif";
color:#1F497D'><o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:10.0pt;color:#1F497D'>Interaction
Developer</span><span style='font-size:12.0pt;font-family:"Times New Roman","serif";
color:#1F497D'><o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:10.0pt;color:#1F497D'>Boomer
Consulting, Inc.</span><span style='font-size:12.0pt;font-family:"Times New Roman","serif";
color:#1F497D'><o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:12.0pt;font-family:"Times New Roman","serif";
color:#1F497D'>&nbsp;<o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:10.0pt;color:#1F497D'>1.785.537.2358
ext. 17</span><span style='font-size:12.0pt;font-family:"Times New Roman","serif";
color:#1F497D'><o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:10.0pt;color:#1F497D'>1.888.266.6375
ext. 17</span><span style='font-size:12.0pt;font-family:"Times New Roman","serif";
color:#1F497D'><o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:10.0pt;color:#1F497D'>1.785.537.4545
(fax)</span><span style='font-size:12.0pt;font-family:"Times New Roman","serif";
color:#1F497D'><o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:12.0pt;font-family:"Times New Roman","serif";
color:#1F497D'>&nbsp;<o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:10.0pt;color:#1F497D'>610 Humboldt</span><span
style='font-size:12.0pt;font-family:"Times New Roman","serif";color:#1F497D'><o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:10.0pt;color:#1F497D'>Manhattan, KS
66502</span><span style='font-size:12.0pt;font-family:"Times New Roman","serif";
color:#1F497D'><o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:12.0pt;font-family:"Times New Roman","serif";
color:#1F497D'>&nbsp;<o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:12.0pt;font-family:"Times New Roman","serif";
color:#1F497D'><a href="http://www.boomer.com/about/team/andrew-hanenkamp.html"><span
style='font-size:10.0pt;font-family:"Calibri","sans-serif"'>http://www.boomer.com/about/team/andrew-hanenkamp.html</span></a><o:p></o:p></span></p>

</div>

<p class=MsoNormal><span style='font-size:10.0pt;color:#1F497D'><a
href="mailto:andrew.hanenkamp@boomer.com">andrew.hanenkamp@boomer.com</a></span><span
style='color:#1F497D'><o:p></o:p></span></p>

<div style='border:none;border-left:solid blue 1.5pt;padding:0in 0in 0in 4.0pt'>

<div>

<div style='border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0in 0in 0in'>

<p class=MsoNormal><b><span style='font-size:10.0pt;font-family:"Tahoma","sans-serif"'>From:</span></b><span
style='font-size:10.0pt;font-family:"Tahoma","sans-serif"'>
jifty-devel-bounces@lists.jifty.org
[mailto:jifty-devel-bounces@lists.jifty.org] <b>On Behalf Of </b>Andrew
Sterling Hanenkamp<br>
<b>Sent:</b> Tuesday, May 08, 2007 4:10 PM<br>
<b>To:</b> jifty-devel@lists.jifty.org<br>
<b>Subject:</b> [jifty-devel] RFC: Access considerations for models<o:p></o:p></span></p>

</div>

</div>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>I&#8217;ve been doing some work with the virtual-models branch
(i.e., Jifty with models stored in the database rather than built in Perl
modules). Part of this work has led to a dilemma on how best to deal with
access to the models tables.<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>The issue is that there is no obvious mechanism for
overriding access control to built-in Jifty classes, such as ModelClass and
ModelClassColumn in the virtual-models branch. The additional problem of access
to the tables that are created &#8220;on the fly&#8221; by ModelClass rows is also
problematic. I hesitate to store code in the database as the primary method of
access control, but that&#8217;s probably an option to consider for the future. An
additional ACL system of some kind might be desirable at some point, but I
don&#8217;t think I&#8217;m ready for that proposal yet. <o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>However, this proposal could offer a good stop-gap for
gaining control over access to both of the built-in Jifty models and custom
models stored in ModelClass. This would also provide a mechanism for
implementing custom ACL systems per-application.<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>The key components of the current access control system are:<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoListParagraph style='text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !supportLists]><span
style='font-family:Symbol'><span style='mso-list:Ignore'>&middot;<span
style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]>current_user_can() &#8211; each model may implement
this method to provide per-model access control<o:p></o:p></p>

<p class=MsoListParagraph style='text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !supportLists]><span
style='font-family:Symbol'><span style='mso-list:Ignore'>&middot;<span
style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]>App::CurrentUser &#8211; the CurrentUser object is
used to represent information about the current user that may be used by
current_user_can() implementations to determine if the current model is
permitted<o:p></o:p></p>

<p class=MsoListParagraph style='margin-left:1.0in;text-indent:-.25in;
mso-list:l0 level2 lfo2'><![if !supportLists]><span style='font-family:"Courier New"'><span
style='mso-list:Ignore'>o<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;
</span></span></span><![endif]>is_bootstrap_user() &#8211; the built-in
implementation of current_user_can() allows the user that isn&#8217;t logged when
administration mode is enabled and during the run of Jifty::Bootstrap to have
unrestricted access<o:p></o:p></p>

<p class=MsoListParagraph style='margin-left:1.0in;text-indent:-.25in;
mso-list:l0 level2 lfo2'><![if !supportLists]><span style='font-family:"Courier New"'><span
style='mso-list:Ignore'>o<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;
</span></span></span><![endif]>is_superuser() &#8211; the built-in implementation of
current_user_can() allows any current user with this set to true to have
unrestricted access<o:p></o:p></p>

<p class=MsoListParagraph style='text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !supportLists]><span
style='font-family:Symbol'><span style='mso-list:Ignore'>&middot;<span
style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]>Jifty::RightsFrom &#8211; this is a helper that is
used to import a method named delegate_current_user_can() into the model it is
applied to, this method is called by the default current_user_can()
implementation to cause the checked model to inherit the current_user_can()
method of another model based upon a related column&#8230; for example, if you
delegate based upon the &#8220;owner&#8221; column, then the current user can create, read,
write, or delete the current model if the current user could do the same
operation on owner object<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>Unless I&#8217;m mistaken, this pretty much sums up the current
system. All the other methods and objects related to security are just
additional helpers or checks based upon these pieces.<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>In order to increase the flexibility of the system one more
level, I propose the addition of a single new method to CurrentUser. Right now
the Jifty::Record::current_user_can() implementation contains this snippet:<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>&nbsp;&nbsp;&nbsp; if (&nbsp;&nbsp;
$self-&gt;current_user-&gt;is_bootstrap_user<o:p></o:p></p>

<p class=MsoNormal>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; or
$self-&gt;current_user-&gt;is_superuser )<o:p></o:p></p>

<p class=MsoNormal>&nbsp;&nbsp;&nbsp; {<o:p></o:p></p>

<p class=MsoNormal>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (1);<o:p></o:p></p>

<p class=MsoNormal>&nbsp;&nbsp;&nbsp; }<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>I would suggest modifying this to read as follows:<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>&nbsp;&nbsp;&nbsp; if
($self-&gt;current_user-&gt;can_access($model, $right, @_))<o:p></o:p></p>

<p class=MsoNormal>&nbsp;&nbsp;&nbsp; {<o:p></o:p></p>

<p class=MsoNormal>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (1);<o:p></o:p></p>

<p class=MsoNormal>&nbsp;&nbsp;&nbsp; }<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>Then, the default Jifty::CurrentUser implementation would
add this new method:<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>sub can_access {<o:p></o:p></p>

<p class=MsoNormal>&nbsp;&nbsp;&nbsp; my $self = shift;<o:p></o:p></p>

<p class=MsoNormal>&nbsp;&nbsp;&nbsp; return $self-&gt;is_bootstrap_user ||
$self-&gt;is_superuser;<o:p></o:p></p>

<p class=MsoNormal>}<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>This has the advantage of not breaking any existing code,
but granting ultimate authority on access control to the current user object. <o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>Alternatively, the previous if-statement could stand as before
and the default can_access() implementation just returns false and is checked
in addition to is_bootstrap_user() and is_superuser(). It makes little
difference to me. <o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>I see this as having the following advantages:<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoListParagraph style='text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !supportLists]><span
style='font-family:Symbol'><span style='mso-list:Ignore'>&middot;<span
style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]>Allows a single method implementation to control
access to all models, which would allow really simple sites to create a single
simple access control implementation for all models in one place.<o:p></o:p></p>

<p class=MsoListParagraph style='text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !supportLists]><span
style='font-family:Symbol'><span style='mso-list:Ignore'>&middot;<span
style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]>Allows a complex application that needs
fine-grained user-level control of access to do so by adding an ACL or other
global access control system in the single method, can_access().<o:p></o:p></p>

<p class=MsoListParagraph style='text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !supportLists]><span
style='font-family:Symbol'><span style='mso-list:Ignore'>&middot;<span
style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]>Allows the implementations using virtual-models
to apply application specific control to database-backed models via a custom
CurrentUser object rather than some particular ACL system or code stored in the
database.<o:p></o:p></p>

<p class=MsoListParagraph style='text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !supportLists]><span
style='font-family:Symbol'><span style='mso-list:Ignore'>&middot;<span
style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]>Allows the implementation of can_access() to
apply application specific control to all built-in Jifty models, such as
ModelClass, ModelClassColumn, Metadata, and Session<o:p></o:p></p>

<p class=MsoListParagraph style='text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !supportLists]><span
style='font-family:Symbol'><span style='mso-list:Ignore'>&middot;<span
style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]>It&#8217;s pretty simple to understand and document.<o:p></o:p></p>

<p class=MsoListParagraph style='text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !supportLists]><span
style='font-family:Symbol'><span style='mso-list:Ignore'>&middot;<span
style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]>It requires a very small patch.<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>The disadvantages I see are:<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoListParagraph style='text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !supportLists]><span
style='font-family:Symbol'><span style='mso-list:Ignore'>&middot;<span
style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]>App::CurrentUser::can_access() becomes
holy-crap-powerful and has the potential to rip your site wide open if you
aren&#8217;t very careful with the implementation.<o:p></o:p></p>

<p class=MsoListParagraph style='text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !supportLists]><span
style='font-family:Symbol'><span style='mso-list:Ignore'>&middot;<span
style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]>One might consider this a violation of the
Separation of Concerns since it pulls access control of models into another
object. However, one might also see it as allowing both objects involved in
access control (model and CurrentUser) having a voice in the control. If you
don&#8217;t want to delegate this authority to CurrentUser, your model&#8217;s
current_user_can() just won&#8217;t use it or the default Jifty::Record
implementation. Built-in models don&#8217;t have this possibility, but at least the
developer has the option of controlling them at all.<o:p></o:p></p>

<p class=MsoListParagraph style='text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !supportLists]><span
style='font-family:Symbol'><span style='mso-list:Ignore'>&middot;<span
style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]>Technically, you can already (mostly) provide
this functionality without any change to Jifty core by overriding App::Record.
However, this doesn&#8217;t allow the application to control access to any modules
that are part of Jifty itself, such as ModelClass, ModelClassColumn, Metadata,
and Session.<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>The only other alternative I can think of to handling this
is to unshift App::Record into the Jifty::Model::* models you want to control
access to and then implement a new default implementation of current_user_can()
that has your application-wide access checks. <o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>As a modified version of that altnerative, I could see
changing the implementation of the built-in Jifty models to inherit from
App::Record automagically allowing this kind of tweak to actually succeed with
the hack. However, that&#8217;s not nearly so simple, so I think my proposal still
stands.<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>If we went with this latter suggestion, I would add an extra
section to the AccessControl manual describing the technique with a possible
reference in the Cookbook too.<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>Please critique.<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal>Cheers,<o:p></o:p></p>

<p class=MsoNormal>Sterling<o:p></o:p></p>

<p class=MsoNormal><o:p>&nbsp;</o:p></p>

<p class=MsoNormal><b><span style='font-size:10.0pt'>--</span></b><span
style='font-size:12.0pt;font-family:"Times New Roman","serif"'><o:p></o:p></span></p>

<p class=MsoNormal><b><span style='font-size:10.0pt'>Andrew Sterling Hanenkamp</span></b><span
style='font-size:12.0pt;font-family:"Times New Roman","serif"'><o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:10.0pt'>Interaction Developer</span><span
style='font-size:12.0pt;font-family:"Times New Roman","serif"'><o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:10.0pt'>Boomer Consulting, Inc.</span><span
style='font-size:12.0pt;font-family:"Times New Roman","serif"'><o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:12.0pt;font-family:"Times New Roman","serif"'>&nbsp;<o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:10.0pt'>1.785.537.2358 ext. 17</span><span
style='font-size:12.0pt;font-family:"Times New Roman","serif"'><o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:10.0pt'>1.888.266.6375 ext. 17</span><span
style='font-size:12.0pt;font-family:"Times New Roman","serif"'><o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:10.0pt'>1.785.537.4545 (fax)</span><span
style='font-size:12.0pt;font-family:"Times New Roman","serif"'><o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:12.0pt;font-family:"Times New Roman","serif"'>&nbsp;<o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:10.0pt'>610 Humboldt</span><span
style='font-size:12.0pt;font-family:"Times New Roman","serif"'><o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:10.0pt'>Manhattan, KS 66502</span><span
style='font-size:12.0pt;font-family:"Times New Roman","serif"'><o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:12.0pt;font-family:"Times New Roman","serif"'>&nbsp;<o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:12.0pt;font-family:"Times New Roman","serif"'><a
href="http://www.boomer.com/about/team/andrew-hanenkamp.html"><span
style='font-size:10.0pt;font-family:"Calibri","sans-serif"'>http://www.boomer.com/about/team/andrew-hanenkamp.html</span></a><o:p></o:p></span></p>

<p class=MsoNormal><span style='font-size:10.0pt'><a
href="mailto:andrew.hanenkamp@boomer.com">andrew.hanenkamp@boomer.com</a></span><o:p></o:p></p>

</div>

</div>

</body>

</html>