~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to doc/core/howto/pb-cred.html

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2006-01-16 19:56:10 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20060116195610-ykmxbia4mnnod9o2
Tags: 2.1.0-0ubuntu2
debian/copyright: Include copyright for python 2.3; some 2.3 files
are included in the upstream tarball, but not in the binary packages.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
<?xml version="1.0"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><title>Twisted Documentation: Authentication with Perspective Broker</title><link href="../howto/stylesheet.css" type="text/css" rel="stylesheet" /></head><body bgcolor="white"><h1 class="title">Authentication with Perspective Broker</h1><div class="toc"><ol><li><a href="#auto0">Overview</a></li><li><a href="#auto1">Compartmentalizing Services</a></li><ul><li><a href="#auto2">Incorrect Arguments</a></li><li><a href="#auto3">Unforgeable References</a></li><li><a href="#auto4">Argument Typechecking</a></li><li><a href="#auto5">Objects as Capabilities</a></li></ul><li><a href="#auto6">Avatars and Perspectives</a></li><li><a href="#auto7">Perspective Examples</a></li><ul><li><a href="#auto8">One Client</a></li><li><a href="#auto9">Two Clients</a></li><li><a href="#auto10">How that example worked</a></li></ul><li><a href="#auto11">Using Avatars</a></li><ul><li><a href="#auto12">Avatar Interfaces</a></li><li><a href="#auto13">Logging Out</a></li><li><a href="#auto14">Making Avatars</a></li><li><a href="#auto15">Connecting and Disconnecting</a></li><li><a href="#auto16">Viewable</a></li><li><a href="#auto17">Chat Server with Avatars</a></li></ul></ol></div><div class="content"><span></span><h2>Overview<a name="auto0"></a></h2><p>The examples shown in <a href="pb-usage.html">Using Perspective
3
 
Broker</a> demonstrate how to do basic remote method calls, but provided no
4
 
facilities for authentication. In this context, authentication is about who
5
 
gets which remote references, and how to restrict access to the <q>right</q>
6
 
set of people or programs.</p><p>As soon as you have a program which offers services to multiple users,
7
 
where those users should not be allowed to interfere with each other, you
8
 
need to think about authentication. Many services use the idea of an
9
 
<q>account</q>, and rely upon fact that each user has access to only one
10
 
account. Twisted uses a system called <a href="cred.html">cred</a> to
11
 
handle authentication issues, and Perspective Broker has code to make it
12
 
easy to implement the most common use cases.</p><h2>Compartmentalizing Services<a name="auto1"></a></h2><p>Imagine how you would write a chat server using PB. The first step might
13
 
be a <code>ChatServer</code> object which had a bunch of
14
 
<code>pb.RemoteReference</code>s that point at user clients. Pretend that
15
 
those clients offered a <code>remote_print</code> method which lets the
16
 
server print a message on the user's console. In that case, the server might
17
 
look something like this:</p><pre class="python">
18
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">ChatServer</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Referenceable</span>):
19
 
 
20
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
21
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span> = {} <span class="py-src-comment"># indexed by name</span>
22
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span> = {} <span class="py-src-comment"># indexed by name</span>
23
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_joinGroup</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">username</span>, <span class="py-src-parameter">groupname</span>):
24
 
        <span class="py-src-keyword">if</span> <span class="py-src-keyword">not</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>.<span class="py-src-variable">has_key</span>(<span class="py-src-variable">groupname</span>):
25
 
            <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>[<span class="py-src-variable">groupname</span>] = []
26
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>[<span class="py-src-variable">groupname</span>].<span class="py-src-variable">append</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">users</span>[<span class="py-src-variable">username</span>])
27
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_sendMessage</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">from_username</span>, <span class="py-src-parameter">groupname</span>, <span class="py-src-parameter">message</span>):
28
 
        <span class="py-src-variable">group</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>[<span class="py-src-variable">groupname</span>]
29
 
        <span class="py-src-keyword">if</span> <span class="py-src-variable">group</span>:
30
 
            <span class="py-src-comment"># send the message to all members of the group
31
 
</span>            <span class="py-src-keyword">for</span> <span class="py-src-variable">user</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">group</span>:
32
 
                <span class="py-src-variable">user</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;print&quot;</span>,
33
 
                                <span class="py-src-string">&quot;&lt;%s&gt; says: %s&quot;</span> % (<span class="py-src-variable">from_username</span>,
34
 
                                                         <span class="py-src-variable">message</span>))
35
 
</pre><p>For now, assume that all clients have somehow acquired a
36
 
<code>pb.RemoteReference</code> to this <code>ChatServer</code> object,
37
 
perhaps using <code>pb.Root</code> and <code>getRootObject</code> as
38
 
described in the <a href="pb-usage.html">previous chapter</a>. In this
39
 
scheme, when a user sends a message to the group, their client runs
40
 
something like the following:</p><pre class="python">
41
 
<span class="py-src-variable">remotegroup</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;sendMessage&quot;</span>, <span class="py-src-string">&quot;alice&quot;</span>, <span class="py-src-string">&quot;Hi, my name is alice.&quot;</span>)
42
 
</pre><h3>Incorrect Arguments<a name="auto2"></a></h3><p>You've probably seen the first problem: users can trivially spoof each
43
 
other. We depend upon the user to pass a correct value in their
44
 
<q>username</q> argument, and have no way to tell if they're lying or not.
45
 
There is nothing to prevent Alice from modifying her client to do:</p><pre class="python">
46
 
<span class="py-src-variable">remotegroup</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;sendMessage&quot;</span>, <span class="py-src-string">&quot;bob&quot;</span>, <span class="py-src-string">&quot;i like pork&quot;</span>)
47
 
</pre><p>much to the horror of Bob's vegetarian friends.<a href="#footnote-1" title="Apparently Alice is one of those weirdos who has nothing better to do than to try and impersonate Bob. She will lie to her chat client, send incorrect objects to remote methods, even rewrite her local client code entirely to accomplish this juvenile prank. Given this adversarial relationship, one must wonder why she and Bob seem to spend so much time together: their adventures are clearly documented by the cryptographic literature."><super>1</super></a></p><p>(In general, learn to get suspicious if you see any argument of a
48
 
remotely-invokable method described as <q>must be X</q>)</p><p>The best way to fix this is to keep track of the user's name locally,
49
 
rather than asking them to send it to the server with each message. The best
50
 
place to keep state is in an object, so this suggests we need a per-user
51
 
object. Rather than choosing an obvious name<a href="#footnote-2" title="the obvious name is clearly ServerSidePerUserObjectWhichNobodyElseHasAccessTo, but because python makes everything else so easy to read, it only seems fair to make your audience work for something"><super>2</super></a>, let's call this the
52
 
<code>User</code> class.
53
 
</p><pre class="python">
54
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">User</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Referenceable</span>):
55
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">username</span>, <span class="py-src-parameter">server</span>, <span class="py-src-parameter">clientref</span>):
56
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">username</span>
57
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">server</span> = <span class="py-src-variable">server</span>
58
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">remote</span> = <span class="py-src-variable">clientref</span>
59
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_joinGroup</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">groupname</span>):
60
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">server</span>.<span class="py-src-variable">joinGroup</span>(<span class="py-src-variable">groupname</span>, <span class="py-src-variable">self</span>)
61
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_sendMessage</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">groupname</span>, <span class="py-src-parameter">message</span>):
62
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">server</span>.<span class="py-src-variable">sendMessage</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">name</span>, <span class="py-src-variable">groupname</span>, <span class="py-src-variable">message</span>)
63
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">send</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">message</span>):
64
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">remote</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;print&quot;</span>, <span class="py-src-variable">message</span>)
65
 
 
66
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">ChatServer</span>:
67
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
68
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span> = {} <span class="py-src-comment"># indexed by name</span>
69
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">joinGroup</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">groupname</span>, <span class="py-src-parameter">user</span>):
70
 
        <span class="py-src-keyword">if</span> <span class="py-src-keyword">not</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>.<span class="py-src-variable">has_key</span>(<span class="py-src-variable">groupname</span>):
71
 
            <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>[<span class="py-src-variable">groupname</span>] = []
72
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>[<span class="py-src-variable">groupname</span>].<span class="py-src-variable">append</span>(<span class="py-src-variable">user</span>)
73
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">sendMessage</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">from_username</span>, <span class="py-src-parameter">groupname</span>, <span class="py-src-parameter">message</span>):
74
 
        <span class="py-src-variable">group</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>[<span class="py-src-variable">groupname</span>]
75
 
        <span class="py-src-keyword">if</span> <span class="py-src-variable">group</span>:
76
 
            <span class="py-src-comment"># send the message to all members of the group
77
 
</span>            <span class="py-src-keyword">for</span> <span class="py-src-variable">user</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">group</span>:
78
 
                <span class="py-src-variable">user</span>.<span class="py-src-variable">send</span>(<span class="py-src-string">&quot;&lt;%s&gt; says: %s&quot;</span> % (<span class="py-src-variable">from_username</span>, <span class="py-src-variable">message</span>))
79
 
</pre><p>Again, assume that each remote client gets access to a single
80
 
<code>User</code> object, which is created with the proper username.</p><p>Note how the <code>ChatServer</code> object has no remote access: it
81
 
isn't even <code>pb.Referenceable</code> anymore. This means that all access
82
 
to it must be mediated through other objects, with code that is under your
83
 
control.</p><p>As long as Alice only has access to her own <code>User</code> object, she
84
 
can no longer spoof Bob. The only way for her to invoke
85
 
<code>ChatServer.sendMessage</code> is to call her <code>User</code>
86
 
object's <code>remote_sendMessage</code> method, and that method uses its
87
 
own state to provide the <code>from_username</code> argument. It doesn't
88
 
give her any way to change that state.</p><p>This restriction is important. The <code>User</code> object is able to
89
 
maintain its own integrity because there is a wall between the object and
90
 
the client: the client cannot inspect or modify internal state, like the
91
 
<code>.name</code> attribute. The only way through this wall is via remote
92
 
method invocations, and the only control Alice has over those invocations is
93
 
when they get invoked and what arguments they are given.</p><div class="note"><strong>Note: </strong><p>No object can maintain its integrity against local threats: by design,
94
 
Python offers no mechanism for class instances to hide their attributes, and
95
 
once an intruder has a copy of <code>self.__dict__</code>, they can do
96
 
everything the original object was able to do.</p></div><h3>Unforgeable References<a name="auto3"></a></h3><p>Now suppose you wanted to implement group parameters, for example a mode
97
 
in which nobody was allowed to talk about mattresses because some users were
98
 
sensitive and calming them down after someone said <q>mattress</q> is a
99
 
hassle that were best avoided altogether. Again, per-group state implies a
100
 
per-group object. We'll go out on a limb and call this the
101
 
<code>Group</code> object:</p><pre class="python">
102
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">User</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Referenceable</span>):
103
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">username</span>, <span class="py-src-parameter">server</span>, <span class="py-src-parameter">clientref</span>):
104
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">username</span>
105
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">server</span> = <span class="py-src-variable">server</span>
106
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">remote</span> = <span class="py-src-variable">clientref</span>
107
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_joinGroup</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">groupname</span>, <span class="py-src-parameter">allowMattress</span>=<span class="py-src-parameter">True</span>):
108
 
        <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">server</span>.<span class="py-src-variable">joinGroup</span>(<span class="py-src-variable">groupname</span>, <span class="py-src-variable">self</span>)
109
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">send</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">message</span>):
110
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">remote</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;print&quot;</span>, <span class="py-src-variable">message</span>)
111
 
 
112
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">Group</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Referenceable</span>):
113
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">groupname</span>, <span class="py-src-parameter">allowMattress</span>):
114
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">groupname</span>
115
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">allowMattress</span> = <span class="py-src-variable">allowMattress</span>
116
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span> = []
117
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_send</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">from_user</span>, <span class="py-src-parameter">message</span>):
118
 
        <span class="py-src-keyword">if</span> <span class="py-src-keyword">not</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">allowMattress</span> <span class="py-src-keyword">and</span> <span class="py-src-variable">message</span>.<span class="py-src-variable">find</span>(<span class="py-src-string">&quot;mattress&quot;</span>) != -<span class="py-src-number">1</span>:
119
 
            <span class="py-src-keyword">raise</span> <span class="py-src-variable">ValueError</span>, <span class="py-src-string">&quot;Don't say that word&quot;</span>
120
 
        <span class="py-src-keyword">for</span> <span class="py-src-variable">user</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span>:
121
 
            <span class="py-src-variable">user</span>.<span class="py-src-variable">send</span>(<span class="py-src-string">&quot;&lt;%s&gt; says: %s&quot;</span> % (<span class="py-src-variable">from_user</span>.<span class="py-src-variable">name</span>, <span class="py-src-variable">message</span>))
122
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">addUser</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">user</span>):
123
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span>.<span class="py-src-variable">append</span>(<span class="py-src-variable">user</span>)
124
 
 
125
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">ChatServer</span>:
126
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
127
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span> = {} <span class="py-src-comment"># indexed by name</span>
128
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">joinGroup</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">groupname</span>, <span class="py-src-parameter">user</span>, <span class="py-src-parameter">allowMattress</span>):
129
 
        <span class="py-src-keyword">if</span> <span class="py-src-keyword">not</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>.<span class="py-src-variable">has_key</span>(<span class="py-src-variable">groupname</span>):
130
 
            <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>[<span class="py-src-variable">groupname</span>] = <span class="py-src-variable">Group</span>(<span class="py-src-variable">groupname</span>, <span class="py-src-variable">allowMattress</span>)
131
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>[<span class="py-src-variable">groupname</span>].<span class="py-src-variable">addUser</span>(<span class="py-src-variable">user</span>)
132
 
        <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>[<span class="py-src-variable">groupname</span>]
133
 
</pre><p>This example takes advantage of the fact that
134
 
<code>pb.Referenceable</code> objects sent over a wire can be returned to
135
 
you, and they will be turned into references to the same object that you
136
 
originally sent. The client cannot modify the object in any way: all they
137
 
can do is point at it and invoke its <code>remote_*</code> methods. Thus,
138
 
you can be sure that the <code>.name</code> attribute remains the same as
139
 
you left it. In this case, the client code would look something like
140
 
this:</p><pre class="python">
141
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">ClientThing</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Referenceable</span>):
142
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_print</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">message</span>):
143
 
        <span class="py-src-keyword">print</span> <span class="py-src-variable">message</span>
144
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">join</span>(<span class="py-src-parameter">self</span>):
145
 
        <span class="py-src-variable">d</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">remoteUser</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;joinGroup&quot;</span>, <span class="py-src-string">&quot;#twisted&quot;</span>,
146
 
                                       <span class="py-src-variable">allowMattress</span>=<span class="py-src-variable">False</span>)
147
 
        <span class="py-src-variable">d</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">gotGroup</span>)
148
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">gotGroup</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">group</span>):
149
 
        <span class="py-src-variable">group</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;send&quot;</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">remoteUser</span>, <span class="py-src-string">&quot;hi everybody&quot;</span>)
150
 
</pre><p>The <code>User</code> object is sent from the server side, and is turned
151
 
into a <code>pb.RemoteReference</code> when it arrives at the client. The
152
 
client sends it back to <code>Group.remote_send</code>, and PB turns it back
153
 
into a reference to the original <code>User</code> when it gets there.
154
 
<code>Group.remote_send</code> can then use its <code>.name</code> attribute
155
 
as the sender of the message.</p><div class="note"><strong>Note: </strong><p>Third party references (there aren't any)</p><p>This technique also relies upon the fact that the
156
 
<code>pb.Referenceable</code> reference can <em>only</em> come from someone
157
 
who holds a corresponding <code>pb.RemoteReference</code>. The design of the
158
 
serialization mechanism (implemented in <code class="API">twisted.spread.jelly</code>: pb, jelly, spread.. get it? Also
159
 
look for <q>banana</q> and <q>marmalade</q>. What other networking framework
160
 
can claim API names based on sandwich ingredients?) makes it impossible for
161
 
a client to obtain a reference that they weren't explicitly given.
162
 
References passed over the wire are given id numbers and recorded in a
163
 
per-connection dictionary. If you didn't give them the reference, the id
164
 
number won't be in the dict, and no amount of guessing by a malicious client
165
 
will give them anything else. The dict goes away when the connection is
166
 
dropped, further limiting the scope of those references.</p><p>Futhermore, it is not possible for Bob to send <em>his</em><code>User</code> reference to Alice (perhaps over some other PB channel
167
 
just between the two of them). Outside the context of Bob's connection to
168
 
the server, that reference is just a meaningless number. To prevent
169
 
confusion, PB will tell you if you try to give it away: when you try to hand
170
 
a <code>pb.RemoteReference</code> to a third party, you'll get an exception
171
 
(implemented with an assert in pb.py:364 RemoteReference.jellyFor).</p><p>This helps the security model somewhat: only the client you gave the
172
 
reference to can cause any damage with it. Of course, the client might be a
173
 
brainless zombie, simply doing anything some third party wants. When it's
174
 
not proxying <code>callRemote</code> invocations, it's probably terrorizing
175
 
the living and searching out human brains for sustenance. In short, if you
176
 
don't trust them, don't give them that reference.</p><p>And remember that everything you've ever given them over that connection
177
 
can come back to you. If expect the client to invoke your method with some
178
 
object A that you sent to them earlier, and instead they send you object B
179
 
(that you also sent to them earlier), and you don't check it somehow, then
180
 
you've just opened up a security hole (we'll see an example of this
181
 
shortly). It may be better to keep such objects in a dictionary on the
182
 
server side, and have the client send you an index string instead. Doing it
183
 
that way makes it obvious that they can send you anything they want, and
184
 
improves the chances that you'll remember to implement the right checks.
185
 
(This is exactly what PB is doing underneath, with a per-connection
186
 
dictionary of <code>Referenceable</code> objects, indexed by a number).</p><p>And, of course, you have to make sure you don't accidentally hand out a
187
 
reference to the wrong object.</p></div><p>But again, note the vulnerability. If Alice holds a
188
 
<code>RemoteReference</code> to <em>any</em> object on the server side that
189
 
has a <code>.name</code> attribute, she can use that name as a spoofed
190
 
<q>from</q> parameter. As a simple example, what if her client code looked
191
 
like:</p><pre class="python">
192
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">ClientThing</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Referenceable</span>):
193
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">join</span>(<span class="py-src-parameter">self</span>):
194
 
        <span class="py-src-variable">d</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">remoteUser</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;joinGroup&quot;</span>, <span class="py-src-string">&quot;#twisted&quot;</span>)
195
 
        <span class="py-src-variable">d</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">gotGroup</span>)
196
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">gotGroup</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">group</span>):
197
 
        <span class="py-src-variable">group</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;send&quot;</span>, <span class="py-src-variable">from_user</span>=<span class="py-src-variable">group</span>, <span class="py-src-string">&quot;hi everybody&quot;</span>)
198
 
</pre><p>This would let her send a message that appeared to come from
199
 
<q>#twisted</q> rather than <q>Alice</q>. If she joined a group that
200
 
happened to be named <q>bob</q> (perhaps it is the <q>How To Be Bob</q>
201
 
channel, populated by Alice and countless others, a place where they can
202
 
share stories about their best impersonating-Bob moments), then she would be
203
 
able to emit a message that looked like <q>&lt;bob&gt; says: hi there</q>,
204
 
and she has accomplished her lifelong goal.</p><h3>Argument Typechecking<a name="auto4"></a></h3><p>There are two techniques to close this hole. The first is to have your
205
 
remotely-invokable methods do type-checking on their arguments: if
206
 
<code>Group.remote_send</code> asserted <code>isinstance(from_user,
207
 
User)</code> then Alice couldn't use non-User objects to do her spoofing,
208
 
and hopefully the rest of the system is designed well enough to prevent her
209
 
from obtaining access to somebody else's User object.</p><h3>Objects as Capabilities<a name="auto5"></a></h3><p>The second technique is to avoid having the client send you the objects
210
 
altogether. If they don't send you anything, there is nothing to verify. In
211
 
this case, you would have to have a per-user-per-group object, in which the
212
 
<code>remote_send</code> method would only take a single
213
 
<code>message</code> argument. The <code>UserGroup</code> object is created
214
 
with references to the only <code>User</code> and <code>Group</code> objects
215
 
that it will ever use, so no lookups are needed:</p><pre class="python">
216
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">UserGroup</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Referenceable</span>):
217
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">user</span>, <span class="py-src-parameter">group</span>):
218
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">user</span> = <span class="py-src-variable">user</span>
219
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">group</span> = <span class="py-src-variable">group</span>
220
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_send</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">message</span>):
221
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">group</span>.<span class="py-src-variable">send</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">user</span>.<span class="py-src-variable">name</span>, <span class="py-src-variable">message</span>)
222
 
 
223
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">Group</span>:
224
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">groupname</span>, <span class="py-src-parameter">allowMattress</span>):
225
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">groupname</span>
226
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">allowMattress</span> = <span class="py-src-variable">allowMattress</span>
227
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span> = []
228
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">send</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">from_user</span>, <span class="py-src-parameter">message</span>):
229
 
        <span class="py-src-keyword">if</span> <span class="py-src-keyword">not</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">allowMattress</span> <span class="py-src-keyword">and</span> <span class="py-src-variable">message</span>.<span class="py-src-variable">find</span>(<span class="py-src-string">&quot;mattress&quot;</span>) != -<span class="py-src-number">1</span>:
230
 
            <span class="py-src-keyword">raise</span> <span class="py-src-variable">ValueError</span>, <span class="py-src-string">&quot;Don't say that word&quot;</span>
231
 
        <span class="py-src-keyword">for</span> <span class="py-src-variable">user</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span>:
232
 
            <span class="py-src-variable">user</span>.<span class="py-src-variable">send</span>(<span class="py-src-string">&quot;&lt;%s&gt; says: %s&quot;</span> % (<span class="py-src-variable">from_user</span>.<span class="py-src-variable">name</span>, <span class="py-src-variable">message</span>))
233
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">addUser</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">user</span>):
234
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span>.<span class="py-src-variable">append</span>(<span class="py-src-variable">user</span>)
235
 
</pre><p>The only message-sending method Alice has left is
236
 
<code>UserGroup.remote_send</code>, and it only accepts a message: there are
237
 
no remaining ways to influence the <q>from</q> name.</p><p>In this model, each remotely-accessible object represents a very small
238
 
set of capabilities. Security is achieved by only granting a minimal set of
239
 
abilities to each remote user.</p><p>PB provides a shortcut which makes this technique easier to use. The
240
 
<code>Viewable</code> class will be discussed <a href="#viewable">below</a>.</p><h2>Avatars and Perspectives<a name="auto6"></a></h2><p>In Twisted's <a href="cred.html">cred</a> system, an <q>Avatar</q> is
241
 
an object that lives on the <q>server</q> side (defined here as the side
242
 
farthest from the human who is trying to get something done) which lets the
243
 
remote user get something done. The avatar isn't really a particular class,
244
 
it's more like a description of a role that some object plays, as in <q>the
245
 
Foo object here is acting as the user's avatar for this particular
246
 
service</q>. Generally, the remote user has some way of getting their avatar
247
 
to run some code. The avatar object may enforce some security checks, and
248
 
provide additional data, then call other methods which get things done.</p><p>The two pieces in the cred puzzle (for any protocol, not just PB) are:
249
 
<q>what serves as the Avatar?</q>, and <q>how does the user get access to
250
 
it?</q>.</p><p>For PB, the first question is easy. The Avatar is a remotely-accessible
251
 
object which can run code: this is a perfect description of
252
 
<code>pb.Referenceable</code> and its subclasses. We shall defer the second
253
 
question until the next section.</p><p>In the example above, you can think of the <code>ChatServer</code> and
254
 
<code>Group</code> objects as a service. The <code>User</code> object is the
255
 
user's server-side representative: everything the user is capable of doing
256
 
is done by running one of its methods. Anything that the server wants to do
257
 
to the user (change their group membership, change their name, delete their
258
 
pet cat, whatever) is done by manipulating the <code>User</code> object.</p><p>There are multiple User objects living in peace and harmony around the
259
 
ChatServer. Each has a different point of view on the services provided by
260
 
the ChatServer and the Groups: each may belong to different groups, some
261
 
might have more permissions than others (like the ability to create groups).
262
 
These different points of view are called <q>Perspectives</q>. This is the
263
 
origin of the term <q>Perspective</q> in <q>Perspective Broker</q>: PB
264
 
provides and controls (i.e. <q>brokers</q>) access to Perspectives.</p><p>Once upon a time, these local-representative objects were actually called
265
 
<code>pb.Perspective</code>. But this has changed with the advent of the
266
 
rewritten cred system, and now the more generic term for a local
267
 
representative object is an Avatar. But you will still see reference to
268
 
<q>Perspective</q> in the code, the docs, and the module names<a href="#footnote-3" title="We could just go ahead and rename Perspective Broker to be Avatar Broker, but 1) that would cause massive compatibility problems, and 2) AB doesn't fit into the whole sandwich-themed naming scheme nearly as well as PB does. If we changed it to AB, we'd probably have to change Banana to be CD (CoderDecoder), and Jelly to be EF (EncapsulatorFragmentor). twisted.spread would then have to be renamed twisted.alphabetsoup, and then the whole food-pun thing would start all over again."><super>3</super></a>. Just remember
269
 
that perspectives and avatars are basically the same thing. </p><p>Despite all we've been <a href="cred.html">telling you</a> about how
270
 
Avatars are more of a concept than an actual class, the base class from
271
 
which you can create your server-side avatar-ish objects is, in fact, named
272
 
<code>pb.Avatar</code><a href="#footnote-4" title="The avatar-ish class is named pb.Avatar because pb.Perspective was already taken, by the (now obsolete) oldcred perspective-ish class. It is a pity, but it simply wasn't possible both replace pb.Perspective in-place and maintain a reasonable level of backwards-compatibility."><super>4</super></a>. These objects behave very much like
273
 
<code>pb.Referenceable</code>. The only difference is that instead of
274
 
offering <q>remote_FOO</q> methods, they offer <q>perspective_FOO</q>
275
 
methods.</p><p>The other way in which <code>pb.Avatar</code> differs from
276
 
<code>pb.Referenceable</code> is that the avatar objects are designed to be
277
 
the first thing retrieved by a cred-using remote client. Just as
278
 
<code>PBClientFactory.getRootObject</code> gives the client access to a
279
 
<code>pb.Root</code> object (which can then provide access to all kinds of
280
 
other objects), <code>PBClientFactory.login</code> gives client access to a
281
 
<code>pb.Avatar</code> object (which can return other references). </p><p>So, the first half of using cred in your PB application is to create an
282
 
Avatar object which implements <code>perspective_</code> methods and is
283
 
careful to do useful things for the remote user while remaining vigilant
284
 
against being tricked with unexpected argument values. It must also be
285
 
careful to never give access to objects that the user should not have access
286
 
to, whether by returning them directly, returning objects which contain
287
 
them, or returning objects which can be asked (remotely) to provide
288
 
them.</p><p>The second half is how the user gets a <code>pb.RemoteReference</code> to
289
 
your Avatar. As explained <a href="cred.html">elsewhere</a>, Avatars are
290
 
obtained from a Realm. The Realm doesn't deal with authentication at all
291
 
(usernames, passwords, public keys, challenge-response systems, retinal
292
 
scanners, real-time DNA sequencers, etc). It simply takes an <q>avatarID</q>
293
 
(which is effectively a username) and returns an Avatar object. The Portal
294
 
and its Checkers deal with authenticating the user: by the time they are
295
 
done, the remote user has proved their right to access the avatarID that is
296
 
given to the Realm, so the Realm can return a remotely-controllable object
297
 
that has whatever powers you wish to grant to this particular user. </p><p>For PB, the realm is expected to return a <code>pb.Avatar</code> (or
298
 
anything which implements <code>pb.IPerspective</code>, really, but there's
299
 
no reason to not return a <code>pb.Avatar</code> subclass). This object will
300
 
be given to the client just like a <code>pb.Root</code> would be without
301
 
cred, and the user can get access to other objects through it (if you let
302
 
them).</p><p>The basic idea is that there is a separate IPerspective-implementing
303
 
object (i.e. the Avatar subclass) (i.e. the <q>perspective</q>) for each
304
 
user, and <em>only</em> the authorized user gets a remote reference to that
305
 
object. You can store whatever permissions or capabilities the user
306
 
possesses in that object, and then use them when the user invokes a remote
307
 
method. You give the user access to the perspective object instead of the
308
 
objects that do the real work.</p><h2>Perspective Examples<a name="auto7"></a></h2><p>Here is a brief example of using a pb.Avatar. Most of the support code
309
 
is magic for now: we'll explain it later.</p><h3>One Client<a name="auto8"></a></h3><div class="py-listing"><pre>
310
 
<span class="py-src-comment">#! /usr/bin/python
311
 
</span>
312
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">spread</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">pb</span>
313
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">cred</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">checkers</span>, <span class="py-src-variable">portal</span>
314
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
315
 
 
316
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyPerspective</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Avatar</span>):
317
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">name</span>):
318
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span>
319
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">perspective_foo</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">arg</span>):
320
 
        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;I am&quot;</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span>, <span class="py-src-string">&quot;perspective_foo(&quot;</span>,<span class="py-src-variable">arg</span>,<span class="py-src-string">&quot;) called on&quot;</span>, <span class="py-src-variable">self</span>
321
 
 
322
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyRealm</span>:
323
 
    <span class="py-src-variable">__implements__</span> = <span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>
324
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarId</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
325
 
        <span class="py-src-keyword">if</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span> <span class="py-src-keyword">not</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">interfaces</span>:
326
 
            <span class="py-src-keyword">raise</span> <span class="py-src-variable">NotImplementedError</span>
327
 
        <span class="py-src-keyword">return</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span>, <span class="py-src-variable">MyPerspective</span>(<span class="py-src-variable">avatarId</span>), <span class="py-src-keyword">lambda</span>:<span class="py-src-variable">None</span>
328
 
 
329
 
<span class="py-src-variable">p</span> = <span class="py-src-variable">portal</span>.<span class="py-src-variable">Portal</span>(<span class="py-src-variable">MyRealm</span>())
330
 
<span class="py-src-variable">p</span>.<span class="py-src-variable">registerChecker</span>(<span class="py-src-variable">checkers</span>.<span class="py-src-variable">InMemoryDatabaseDontUse</span>(<span class="py-src-variable">user1</span>=<span class="py-src-string">&quot;pass1&quot;</span>))
331
 
<span class="py-src-variable">reactor</span>.<span class="py-src-variable">listenTCP</span>(<span class="py-src-number">8800</span>, <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBServerFactory</span>(<span class="py-src-variable">p</span>))
332
 
<span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
333
 
</pre><div class="caption">Source listing - <a href="listings/pb/pb5server.py"><span class="filename">listings/pb/pb5server.py</span></a></div></div><div class="py-listing"><pre>
334
 
<span class="py-src-comment">#! /usr/bin/python
335
 
</span>
336
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">spread</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">pb</span>
337
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
338
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">cred</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">credentials</span>
339
 
 
340
 
<span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
341
 
    <span class="py-src-variable">factory</span> = <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBClientFactory</span>()
342
 
    <span class="py-src-variable">reactor</span>.<span class="py-src-variable">connectTCP</span>(<span class="py-src-string">&quot;localhost&quot;</span>, <span class="py-src-number">8800</span>, <span class="py-src-variable">factory</span>)
343
 
    <span class="py-src-variable">def1</span> = <span class="py-src-variable">factory</span>.<span class="py-src-variable">login</span>(<span class="py-src-variable">credentials</span>.<span class="py-src-variable">UsernamePassword</span>(<span class="py-src-string">&quot;user1&quot;</span>, <span class="py-src-string">&quot;pass1&quot;</span>))
344
 
    <span class="py-src-variable">def1</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">connected</span>)
345
 
    <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
346
 
 
347
 
<span class="py-src-keyword">def</span> <span class="py-src-identifier">connected</span>(<span class="py-src-parameter">perspective</span>):
348
 
    <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;got perspective ref:&quot;</span>, <span class="py-src-variable">perspective</span>
349
 
    <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;asking it to foo(12)&quot;</span>
350
 
    <span class="py-src-variable">perspective</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;foo&quot;</span>, <span class="py-src-number">12</span>)
351
 
 
352
 
<span class="py-src-variable">main</span>()
353
 
</pre><div class="caption">Source listing - <a href="listings/pb/pb5client.py"><span class="filename">listings/pb/pb5client.py</span></a></div></div><p>Ok, so that wasn't really very exciting. It doesn't accomplish much more
354
 
than the first PB example, and used a lot more code to do it. Let's try it
355
 
again with two users this time.</p><div class="note"><strong>Note: </strong><p>When the client runs <code>login</code> to request the Perspective,
356
 
they can provide it with an optional <code>client</code> argument (which
357
 
must be a <code>pb.Referenceable</code> object). If they do, then a
358
 
reference to that object will be handed to the realm's
359
 
<code>requestAvatar</code> in the <code>mind</code> argument.</p><p>The server-side Perspective can use it to invoke remote methods on
360
 
something in the client, so that the client doesn't always have to drive the
361
 
interaction. In a chat server, the client object would be the one to which
362
 
<q>display text</q> messages were sent. In a board game server, this would
363
 
provide a way to tell the clients that someone has made a move, so they can
364
 
update their game boards.</p></div><h3>Two Clients<a name="auto9"></a></h3><div class="py-listing"><pre>
365
 
<span class="py-src-comment">#! /usr/bin/python
366
 
</span>
367
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">spread</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">pb</span>
368
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">cred</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">checkers</span>, <span class="py-src-variable">portal</span>
369
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
370
 
 
371
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyPerspective</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Avatar</span>):
372
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">name</span>):
373
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span>
374
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">perspective_foo</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">arg</span>):
375
 
        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;I am&quot;</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span>, <span class="py-src-string">&quot;perspective_foo(&quot;</span>,<span class="py-src-variable">arg</span>,<span class="py-src-string">&quot;) called on&quot;</span>, <span class="py-src-variable">self</span>
376
 
 
377
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyRealm</span>:
378
 
    <span class="py-src-variable">__implements__</span> = <span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>
379
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarId</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
380
 
        <span class="py-src-keyword">if</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span> <span class="py-src-keyword">not</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">interfaces</span>:
381
 
            <span class="py-src-keyword">raise</span> <span class="py-src-variable">NotImplementedError</span>
382
 
        <span class="py-src-keyword">return</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span>, <span class="py-src-variable">MyPerspective</span>(<span class="py-src-variable">avatarId</span>), <span class="py-src-keyword">lambda</span>:<span class="py-src-variable">None</span>
383
 
 
384
 
<span class="py-src-variable">p</span> = <span class="py-src-variable">portal</span>.<span class="py-src-variable">Portal</span>(<span class="py-src-variable">MyRealm</span>())
385
 
<span class="py-src-variable">c</span> = <span class="py-src-variable">checkers</span>.<span class="py-src-variable">InMemoryUsernamePasswordDatabaseDontUse</span>(<span class="py-src-variable">user1</span>=<span class="py-src-string">&quot;pass1&quot;</span>,
386
 
                                                     <span class="py-src-variable">user2</span>=<span class="py-src-string">&quot;pass2&quot;</span>)
387
 
<span class="py-src-variable">p</span>.<span class="py-src-variable">registerChecker</span>(<span class="py-src-variable">c</span>)
388
 
<span class="py-src-variable">reactor</span>.<span class="py-src-variable">listenTCP</span>(<span class="py-src-number">8800</span>, <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBServerFactory</span>(<span class="py-src-variable">p</span>))
389
 
<span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
390
 
</pre><div class="caption">Source listing - <a href="listings/pb/pb6server.py"><span class="filename">listings/pb/pb6server.py</span></a></div></div><div class="py-listing"><pre>
391
 
<span class="py-src-comment">#! /usr/bin/python
392
 
</span>
393
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">spread</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">pb</span>
394
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
395
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">cred</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">credentials</span>
396
 
 
397
 
<span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
398
 
    <span class="py-src-variable">factory</span> = <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBClientFactory</span>()
399
 
    <span class="py-src-variable">reactor</span>.<span class="py-src-variable">connectTCP</span>(<span class="py-src-string">&quot;localhost&quot;</span>, <span class="py-src-number">8800</span>, <span class="py-src-variable">factory</span>)
400
 
    <span class="py-src-variable">def1</span> = <span class="py-src-variable">factory</span>.<span class="py-src-variable">login</span>(<span class="py-src-variable">credentials</span>.<span class="py-src-variable">UsernamePassword</span>(<span class="py-src-string">&quot;user1&quot;</span>, <span class="py-src-string">&quot;pass1&quot;</span>))
401
 
    <span class="py-src-variable">def1</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">connected</span>)
402
 
    <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
403
 
 
404
 
<span class="py-src-keyword">def</span> <span class="py-src-identifier">connected</span>(<span class="py-src-parameter">perspective</span>):
405
 
    <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;got perspective1 ref:&quot;</span>, <span class="py-src-variable">perspective</span>
406
 
    <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;asking it to foo(13)&quot;</span>
407
 
    <span class="py-src-variable">perspective</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;foo&quot;</span>, <span class="py-src-number">13</span>)
408
 
 
409
 
<span class="py-src-variable">main</span>()
410
 
</pre><div class="caption">Source listing - <a href="listings/pb/pb6client1.py"><span class="filename">listings/pb/pb6client1.py</span></a></div></div><div class="py-listing"><pre>
411
 
<span class="py-src-comment">#! /usr/bin/python
412
 
</span>
413
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">spread</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">pb</span>
414
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
415
 
 
416
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">spread</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">pb</span>
417
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
418
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">cred</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">credentials</span>
419
 
 
420
 
<span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
421
 
    <span class="py-src-variable">factory</span> = <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBClientFactory</span>()
422
 
    <span class="py-src-variable">reactor</span>.<span class="py-src-variable">connectTCP</span>(<span class="py-src-string">&quot;localhost&quot;</span>, <span class="py-src-number">8800</span>, <span class="py-src-variable">factory</span>)
423
 
    <span class="py-src-variable">def1</span> = <span class="py-src-variable">factory</span>.<span class="py-src-variable">login</span>(<span class="py-src-variable">credentials</span>.<span class="py-src-variable">UsernamePassword</span>(<span class="py-src-string">&quot;user2&quot;</span>, <span class="py-src-string">&quot;pass2&quot;</span>))
424
 
    <span class="py-src-variable">def1</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">connected</span>)
425
 
    <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
426
 
 
427
 
<span class="py-src-keyword">def</span> <span class="py-src-identifier">connected</span>(<span class="py-src-parameter">perspective</span>):
428
 
    <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;got perspective2 ref:&quot;</span>, <span class="py-src-variable">perspective</span>
429
 
    <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;asking it to foo(14)&quot;</span>
430
 
    <span class="py-src-variable">perspective</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;foo&quot;</span>, <span class="py-src-number">14</span>)
431
 
 
432
 
<span class="py-src-variable">main</span>()
433
 
</pre><div class="caption">Source listing - <a href="listings/pb/pb6client2.py"><span class="filename">listings/pb/pb6client2.py</span></a></div></div><p>While pb6server.py is running, try starting pb6client1, then pb6client2.
434
 
Compare the argument passed by the <code>.callRemote()</code> in each
435
 
client. You can see how each client gets connected to a different
436
 
Perspective.</p><h3>How that example worked<a name="auto10"></a></h3><a name="smallexample"></a><p>Let's walk through the previous example and see what was going on.</p><p>First, we created a subclass called <code>MyPerspective</code> which is
437
 
our server-side Avatar. It implements a <code>perspective_foo</code> method
438
 
that is exposed to the remote client.</p><p>Second, we created a realm (an object which implements
439
 
<code>IRealm</code>, and therefore implements <code>requestAvatar</code>).
440
 
This realm manufactures <code>MyPerspective</code> objects. It makes as many
441
 
as we want, and names each one with the avatarID (a username) that comes out
442
 
of the checkers. This MyRealm object returns two other objects as well,
443
 
which we will describe later.</p><p>Third, we created a portal to hold this realm. The portal's job is to
444
 
dispatch incoming clients to the credential checkers, and then to request
445
 
Avatars for any which survive the authentication process.</p><p>Fourth, we made a simple checker (an object which implements
446
 
<code>IChecker</code>) to hold valid user/password pairs. The checker gets
447
 
registered with the portal, so it knows who to ask when new clients connect.
448
 
We use a checker named <code>InMemoryDatabaseDontUse</code>, which suggests
449
 
that 1: all the username/password pairs are kept in memory instead of being
450
 
saved to a database or something, and 2: you shouldn't use it. The
451
 
admonition against using it is because there are better schemes: keeping
452
 
everything in memory will not work when you have thousands or millions of
453
 
users to keep track of, the passwords will be stored in the .tap file when
454
 
the application shuts down (possibly a security risk), and finally it is a
455
 
nuisance to add or remove users after the checker is constructed.</p><p>Fifth, we create a <code>pb.PBServerFactory</code> to listen on a TCP
456
 
port. This factory knows how to connect the remote client to the Portal, so
457
 
incoming connections will be handed to the authentication process. Other
458
 
protocols (non-PB) would do something similar: the factory that creates
459
 
Protocol objects will give those objects access to the Portal so
460
 
authentication can take place.</p><p>On the client side, a <code>pb.PBClientFactory</code> is created (as <a href="pb-usage.html">before</a>) and attached to a TCP connection. When the
461
 
connection completes, the factory will be asked to produce a Protocol, and
462
 
it will create a PB object. Unlike the previous chapter, where we used
463
 
<code>.getRootObject</code>, here we use <code>factory.login</code> to
464
 
initiate the cred authentication process. We provide a
465
 
<code>credentials</code> object, which is the client-side agent for doing
466
 
our half of the authentication process. This process may involve several
467
 
messages: challenges, responses, encrypted passwords, secure hashes, etc. We
468
 
give our credentials object everything it will need to respond correctly (in
469
 
this case, a username and password, but you could write a credential that
470
 
used public-key encryption or even fancier techniques).</p><p><code>login</code> returns a Deferred which, when it fires, will return a
471
 
<code>pb.RemoteReference</code> to the remote avatar. We can then do
472
 
<code>callRemote</code> to invoke a <code>perspective_foo</code> method on
473
 
that Avatar.</p><h2>Using Avatars<a name="auto11"></a></h2><h3>Avatar Interfaces<a name="auto12"></a></h3><p>The first element of the 3-tuple returned by <code>requestAvatar</code>
474
 
indicates which Interface this Avatar implements. For PB avatars, it will
475
 
always be <code>pb.IPerspective</code>, because that's the only interface
476
 
these avatars implement.</p><p>This element is present because <code>requestAvatar</code> is actually
477
 
presented with a list of possible Interfaces. The question being posed to
478
 
the Realm is: <q>do you have an avatar for (avatarID) that can implement one
479
 
of the following set of Interfaces?</q>. Some portals and checkers might
480
 
give a list of Interfaces and the Realm could pick; the PB code only knows
481
 
how to do one, so we cannot take advantage of this feature.</p><h3>Logging Out<a name="auto13"></a></h3><p>The third element of the 3-tuple is a zero-argument callable, which will
482
 
be invoked by the protocol when the connection has been lost. We can use
483
 
this to notify the Avatar when the client has lost its connection. This will
484
 
be described in more detail below.</p><h3>Making Avatars<a name="auto14"></a></h3><p>In the example above, we create Avatars upon request, during
485
 
<code>requestAvatar</code>. Depending upon the service, these Avatars might
486
 
already exist before the connection is received, and might outlive the
487
 
connection. The Avatars might also accept multiple connections.</p><p>Another possibility is that the Avatars might exist ahead of time, but in
488
 
a different form (frozen in a pickle and/or saved in a database). In this
489
 
case, <code>requestAvatar</code> may need to perform a database lookup and
490
 
then do something with the result before it can provide an avatar. In this
491
 
case, it would probably return a Deferred so it could provide the real
492
 
Avatar later, once the lookup had completed.</p><p>Here are some possible implementations of
493
 
<code>MyRealm.requestAvatar</code>:</p><pre class="python">
494
 
<span class="py-src-comment"># pre-existing, static avatars
495
 
</span>    <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarID</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
496
 
        <span class="py-src-keyword">assert</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">interfaces</span>
497
 
        <span class="py-src-variable">avatar</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span>[<span class="py-src-variable">avatarID</span>]
498
 
        <span class="py-src-keyword">return</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span>, <span class="py-src-variable">avatar</span>, <span class="py-src-keyword">lambda</span>:<span class="py-src-variable">None</span>
499
 
 
500
 
    <span class="py-src-comment"># database lookup and unpickling
501
 
</span>    <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarID</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
502
 
        <span class="py-src-keyword">assert</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">interfaces</span>
503
 
        <span class="py-src-variable">d</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">database</span>.<span class="py-src-variable">fetchAvatar</span>(<span class="py-src-variable">avatarID</span>)
504
 
        <span class="py-src-variable">d</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">doUnpickle</span>)
505
 
        <span class="py-src-keyword">return</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span>, <span class="py-src-variable">d</span>, <span class="py-src-keyword">lambda</span>:<span class="py-src-variable">None</span>
506
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">doUnpickle</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">pickled</span>):
507
 
        <span class="py-src-variable">avatar</span> = <span class="py-src-variable">pickle</span>.<span class="py-src-variable">loads</span>(<span class="py-src-variable">pickled</span>)
508
 
        <span class="py-src-keyword">return</span> <span class="py-src-variable">avatar</span>
509
 
 
510
 
    <span class="py-src-comment"># everybody shares the same Avatar
511
 
</span>    <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarID</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
512
 
        <span class="py-src-keyword">assert</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">interfaces</span>
513
 
        <span class="py-src-keyword">return</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">theOneAvatar</span>, <span class="py-src-keyword">lambda</span>:<span class="py-src-variable">None</span>
514
 
 
515
 
    <span class="py-src-comment"># anonymous users share one Avatar, named users each get their own
516
 
</span>    <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarID</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
517
 
        <span class="py-src-keyword">assert</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">interfaces</span>
518
 
        <span class="py-src-keyword">if</span> <span class="py-src-variable">avatarID</span> == <span class="py-src-variable">checkers</span>.<span class="py-src-variable">ANONYMOUS</span>:
519
 
            <span class="py-src-keyword">return</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">anonAvatar</span>, <span class="py-src-keyword">lambda</span>:<span class="py-src-variable">None</span>
520
 
        <span class="py-src-keyword">else</span>:
521
 
            <span class="py-src-keyword">return</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span>[<span class="py-src-variable">avatarID</span>], <span class="py-src-keyword">lambda</span>:<span class="py-src-variable">None</span>
522
 
 
523
 
    <span class="py-src-comment"># anonymous users get independent (but temporary) Avatars
524
 
</span>    <span class="py-src-comment"># named users get their own persistent one
525
 
</span>    <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarID</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
526
 
        <span class="py-src-keyword">assert</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">interfaces</span>
527
 
        <span class="py-src-keyword">if</span> <span class="py-src-variable">avatarID</span> == <span class="py-src-variable">checkers</span>.<span class="py-src-variable">ANONYMOUS</span>:
528
 
            <span class="py-src-keyword">return</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span>, <span class="py-src-variable">MyAvatar</span>(), <span class="py-src-keyword">lambda</span>:<span class="py-src-variable">None</span>
529
 
        <span class="py-src-keyword">else</span>:
530
 
            <span class="py-src-keyword">return</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span>, <span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span>[<span class="py-src-variable">avatarID</span>], <span class="py-src-keyword">lambda</span>:<span class="py-src-variable">None</span>
531
 
</pre><p>The last example, note that the new <code>MyAvatar</code> instance is not
532
 
saved anywhere: it will vanish when the connection is dropped. By contrast,
533
 
the avatars that live in the <code>self.avatars</code> dictionary will
534
 
probably get persisted into the .tap file along with the Realm, the Portal,
535
 
and anything else that is referenced by the top-level Application object.
536
 
This is an easy way to manage saved user profiles.</p><h3>Connecting and Disconnecting<a name="auto15"></a></h3><p>It may be useful for your Avatars to be told when remote clients gain
537
 
(and lose) access to them. For example, and Avatar might be updated by
538
 
something in the server, and if there are clients attached, it should update
539
 
them (through the <q>mind</q> argument which lets the Avatar do callRemote
540
 
on the client).</p><p>One common idiom which accomplishes this is to have the Realm tell the
541
 
avatar that a remote client has just attached. The Realm can also ask the
542
 
protocol to let it know when the connection goes away, so it can then inform
543
 
the Avatar that the client has detached. The third member of the
544
 
<code>requestAvatar</code> return tuple is a callable which will be invoked
545
 
when the connection is lost.</p><pre class="python">
546
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyPerspective</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Avatar</span>):
547
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
548
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">clients</span> = []
549
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">attached</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">mind</span>):
550
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">clients</span>.<span class="py-src-variable">append</span>(<span class="py-src-variable">mind</span>)
551
 
        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;attached to&quot;</span>, <span class="py-src-variable">mind</span>
552
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">detached</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">mind</span>):
553
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">clients</span>.<span class="py-src-variable">remove</span>(<span class="py-src-variable">mind</span>)
554
 
        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;detached from&quot;</span>, <span class="py-src-variable">mind</span>
555
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">update</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">message</span>):
556
 
        <span class="py-src-keyword">for</span> <span class="py-src-variable">c</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">clients</span>:
557
 
            <span class="py-src-variable">c</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;update&quot;</span>, <span class="py-src-variable">message</span>)
558
 
 
559
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyRealm</span>:
560
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarID</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
561
 
        <span class="py-src-keyword">assert</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">interfaces</span>
562
 
        <span class="py-src-variable">avatar</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">avatars</span>[<span class="py-src-variable">avatarID</span>]
563
 
        <span class="py-src-variable">avatar</span>.<span class="py-src-variable">attached</span>(<span class="py-src-variable">mind</span>)
564
 
        <span class="py-src-keyword">return</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span>, <span class="py-src-variable">avatar</span>, <span class="py-src-keyword">lambda</span> <span class="py-src-variable">a</span>=<span class="py-src-variable">avatar</span>:<span class="py-src-variable">a</span>.<span class="py-src-variable">detached</span>(<span class="py-src-variable">mind</span>)
565
 
</pre><h3>Viewable<a name="auto16"></a></h3><a name="viewable"></a><p>Once you have <code>IPerspective</code> objects (i.e. the Avatar) to
566
 
represent users, the <code base="twisted.spread.flavors" class="API">Viewable</code> class can come into play. This
567
 
class behaves a lot like <code>Referenceable</code>: it turns into a
568
 
<code>RemoteReference</code> when sent over the wire, and certain methods
569
 
can be invoked by the holder of that reference. However, the methods that
570
 
can be called have names that start with <code>view_</code> instead of
571
 
<code>remote_</code>, and those methods are always called with an extra
572
 
<code>perspective</code> argument that points to the Avatar through which
573
 
the reference was sent:</p><pre class="python">
574
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">Foo</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Viewable</span>):
575
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">view_doFoo</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">perspective</span>, <span class="py-src-parameter">arg1</span>, <span class="py-src-parameter">arg2</span>):
576
 
        <span class="py-src-keyword">pass</span>
577
 
</pre><p>This is useful if you want to let multiple clients share a reference to
578
 
the same object. The <code>view_</code> methods can use the
579
 
<q>perspective</q> argument to figure out which client is calling them. This
580
 
gives them a way to do additional permission checks, do per-user accounting,
581
 
etc.</p><p>This is the shortcut which makes per-user-per-group capability objects
582
 
much easier to use. Instead of creating such per-(user,group) objects, you
583
 
just have per-group objects which inherit from <code>pb.Viewable</code>, and
584
 
give the user references to them. The local <code>pb.Avatar</code> object
585
 
will automatically show up as the <q>perspective</q> argument in the
586
 
<code>view_*</code> method calls, give you a chance to involve the Avatar in
587
 
the process.</p><h3>Chat Server with Avatars<a name="auto17"></a></h3><p>Combining all the above techniques, here is an example chat server which
588
 
uses a fixed set of identities (say, for the three members of your bridge
589
 
club, who hang out in <q>#NeedAFourth</q> hoping that someone will discover
590
 
your server, guess somebody's password, break in, join the group, and also
591
 
be available for a game next saturday afternoon).</p><div class="py-listing"><pre>
592
 
<span class="py-src-comment">#! /usr/bin/python
593
 
</span>
594
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">cred</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">portal</span>, <span class="py-src-variable">checkers</span>
595
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">spread</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">pb</span>
596
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
597
 
 
598
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">ChatServer</span>:
599
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
600
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span> = {} <span class="py-src-comment"># indexed by name</span>
601
 
 
602
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">joinGroup</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">groupname</span>, <span class="py-src-parameter">user</span>, <span class="py-src-parameter">allowMattress</span>):
603
 
        <span class="py-src-keyword">if</span> <span class="py-src-keyword">not</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>.<span class="py-src-variable">has_key</span>(<span class="py-src-variable">groupname</span>):
604
 
            <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>[<span class="py-src-variable">groupname</span>] = <span class="py-src-variable">Group</span>(<span class="py-src-variable">groupname</span>, <span class="py-src-variable">allowMattress</span>)
605
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>[<span class="py-src-variable">groupname</span>].<span class="py-src-variable">addUser</span>(<span class="py-src-variable">user</span>)
606
 
        <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>[<span class="py-src-variable">groupname</span>]
607
 
 
608
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">ChatRealm</span>:
609
 
    <span class="py-src-variable">__implements__</span> = <span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>
610
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarID</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
611
 
        <span class="py-src-keyword">assert</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">interfaces</span>
612
 
        <span class="py-src-variable">avatar</span> = <span class="py-src-variable">User</span>(<span class="py-src-variable">avatarID</span>)
613
 
        <span class="py-src-variable">avatar</span>.<span class="py-src-variable">server</span> = <span class="py-src-variable">self</span>.<span class="py-src-variable">server</span>
614
 
        <span class="py-src-variable">avatar</span>.<span class="py-src-variable">attached</span>(<span class="py-src-variable">mind</span>)
615
 
        <span class="py-src-keyword">return</span> <span class="py-src-variable">pb</span>.<span class="py-src-variable">IPerspective</span>, <span class="py-src-variable">avatar</span>, <span class="py-src-keyword">lambda</span> <span class="py-src-variable">a</span>=<span class="py-src-variable">avatar</span>:<span class="py-src-variable">a</span>.<span class="py-src-variable">detached</span>(<span class="py-src-variable">mind</span>)
616
 
 
617
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">User</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Avatar</span>):
618
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">name</span>):
619
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span>
620
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">attached</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">mind</span>):
621
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">remote</span> = <span class="py-src-variable">mind</span>
622
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">detached</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">mind</span>):
623
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">remote</span> = <span class="py-src-variable">None</span>
624
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">perspective_joinGroup</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">groupname</span>, <span class="py-src-parameter">allowMattress</span>=<span class="py-src-parameter">True</span>):
625
 
        <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">server</span>.<span class="py-src-variable">joinGroup</span>(<span class="py-src-variable">groupname</span>, <span class="py-src-variable">self</span>, <span class="py-src-variable">allowMattress</span>)
626
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">send</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">message</span>):
627
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">remote</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;print&quot;</span>, <span class="py-src-variable">message</span>)
628
 
 
629
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">Group</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Viewable</span>):
630
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">groupname</span>, <span class="py-src-parameter">allowMattress</span>):
631
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">groupname</span>
632
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">allowMattress</span> = <span class="py-src-variable">allowMattress</span>
633
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span> = []
634
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">addUser</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">user</span>):
635
 
        <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span>.<span class="py-src-variable">append</span>(<span class="py-src-variable">user</span>)
636
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">view_send</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">from_user</span>, <span class="py-src-parameter">message</span>):
637
 
        <span class="py-src-keyword">if</span> <span class="py-src-keyword">not</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">allowMattress</span> <span class="py-src-keyword">and</span> <span class="py-src-variable">message</span>.<span class="py-src-variable">find</span>(<span class="py-src-string">&quot;mattress&quot;</span>) != -<span class="py-src-number">1</span>:
638
 
            <span class="py-src-keyword">raise</span> <span class="py-src-variable">ValueError</span>, <span class="py-src-string">&quot;Don't say that word&quot;</span>
639
 
        <span class="py-src-keyword">for</span> <span class="py-src-variable">user</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span>:
640
 
            <span class="py-src-variable">user</span>.<span class="py-src-variable">send</span>(<span class="py-src-string">&quot;&lt;%s&gt; says: %s&quot;</span> % (<span class="py-src-variable">from_user</span>.<span class="py-src-variable">name</span>, <span class="py-src-variable">message</span>))
641
 
 
642
 
<span class="py-src-variable">realm</span> = <span class="py-src-variable">ChatRealm</span>()
643
 
<span class="py-src-variable">realm</span>.<span class="py-src-variable">server</span> = <span class="py-src-variable">ChatServer</span>()
644
 
<span class="py-src-variable">checker</span> = <span class="py-src-variable">checkers</span>.<span class="py-src-variable">InMemoryUsernamePasswordDatabaseDontUse</span>()
645
 
<span class="py-src-variable">checker</span>.<span class="py-src-variable">addUser</span>(<span class="py-src-string">&quot;alice&quot;</span>, <span class="py-src-string">&quot;1234&quot;</span>)
646
 
<span class="py-src-variable">checker</span>.<span class="py-src-variable">addUser</span>(<span class="py-src-string">&quot;bob&quot;</span>, <span class="py-src-string">&quot;secret&quot;</span>)
647
 
<span class="py-src-variable">checker</span>.<span class="py-src-variable">addUser</span>(<span class="py-src-string">&quot;carol&quot;</span>, <span class="py-src-string">&quot;fido&quot;</span>)
648
 
<span class="py-src-variable">p</span> = <span class="py-src-variable">portal</span>.<span class="py-src-variable">Portal</span>(<span class="py-src-variable">realm</span>, [<span class="py-src-variable">checker</span>])
649
 
 
650
 
<span class="py-src-variable">reactor</span>.<span class="py-src-variable">listenTCP</span>(<span class="py-src-number">8800</span>, <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBServerFactory</span>(<span class="py-src-variable">p</span>))
651
 
<span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
652
 
</pre><div class="caption">Source listing - <a href="listings/pb/chatserver.py"><span class="filename">listings/pb/chatserver.py</span></a></div></div><p>Notice that the client uses <code>perspective_joinGroup</code> to both
653
 
join a group and retrieve a <code>RemoteReference</code> to the
654
 
<code>Group</code> object. However, the reference they get is actually to a
655
 
special intermediate object called a <code>pb.ViewPoint</code>. When they do
656
 
<code>group.callRemote(&quot;send&quot;, &quot;message&quot;)</code>, their avatar is inserted
657
 
into the argument list that <code>Group.view_send</code> actually sees. This
658
 
lets the group get their username out of the Avatar without giving the
659
 
client an opportunity to spoof someone else.</p><p>The client side code that joins a group and sends a message would look
660
 
like this:</p><div class="py-listing"><pre>
661
 
<span class="py-src-comment">#! /usr/bin/python
662
 
</span>
663
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">spread</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">pb</span>
664
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
665
 
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">cred</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">credentials</span>
666
 
 
667
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">Client</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Referenceable</span>):
668
 
 
669
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_print</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">message</span>):
670
 
        <span class="py-src-keyword">print</span> <span class="py-src-variable">message</span>
671
 
 
672
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">connect</span>(<span class="py-src-parameter">self</span>):
673
 
        <span class="py-src-variable">factory</span> = <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBClientFactory</span>()
674
 
        <span class="py-src-variable">reactor</span>.<span class="py-src-variable">connectTCP</span>(<span class="py-src-string">&quot;localhost&quot;</span>, <span class="py-src-number">8800</span>, <span class="py-src-variable">factory</span>)
675
 
        <span class="py-src-variable">def1</span> = <span class="py-src-variable">factory</span>.<span class="py-src-variable">login</span>(<span class="py-src-variable">credentials</span>.<span class="py-src-variable">UsernamePassword</span>(<span class="py-src-string">&quot;alice&quot;</span>, <span class="py-src-string">&quot;1234&quot;</span>),
676
 
                             <span class="py-src-variable">client</span>=<span class="py-src-variable">self</span>)
677
 
        <span class="py-src-variable">def1</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">connected</span>)
678
 
        <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
679
 
 
680
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">connected</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">perspective</span>):
681
 
        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;connected, joining group #lookingForFourth&quot;</span>
682
 
        <span class="py-src-comment"># this perspective is a reference to our User object
683
 
</span>        <span class="py-src-variable">d</span> = <span class="py-src-variable">perspective</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;joinGroup&quot;</span>, <span class="py-src-string">&quot;#lookingForFourth&quot;</span>)
684
 
        <span class="py-src-variable">d</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">gotGroup</span>)
685
 
 
686
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">gotGroup</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">group</span>):
687
 
        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;joined group, now sending a message to all members&quot;</span>
688
 
        <span class="py-src-comment"># 'group' is a reference to the Group object (through a ViewPoint)
689
 
</span>        <span class="py-src-variable">d</span> = <span class="py-src-variable">group</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;send&quot;</span>, <span class="py-src-string">&quot;You can call me Al.&quot;</span>)
690
 
        <span class="py-src-variable">d</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">shutdown</span>)
691
 
 
692
 
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">shutdown</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">result</span>):
693
 
        <span class="py-src-variable">reactor</span>.<span class="py-src-variable">stop</span>()
694
 
 
695
 
 
696
 
<span class="py-src-variable">Client</span>().<span class="py-src-variable">connect</span>()
697
 
</pre><div class="caption">Source listing - <a href="listings/pb/chatclient.py"><span class="filename">listings/pb/chatclient.py</span></a></div></div><h2>Footnotes</h2><ol><li><a name="footnote-1"><span xmlns="http://www.w3.org/1999/xhtml" class="footnote">Apparently Alice is one of those weirdos who has nothing
698
 
better to do than to try and impersonate Bob. She will lie to her chat
699
 
client, send incorrect objects to remote methods, even rewrite her local
700
 
client code entirely to accomplish this juvenile prank. Given this
701
 
adversarial relationship, one must wonder why she and Bob seem to spend so
702
 
much time together: their adventures are clearly documented by the
703
 
cryptographic literature.</span></a></li><li><a name="footnote-2"><span xmlns="http://www.w3.org/1999/xhtml" class="footnote">the
704
 
obvious name is clearly
705
 
<code>ServerSidePerUserObjectWhichNobodyElseHasAccessTo</code>, but because
706
 
python makes everything else so easy to read, it only seems fair to make
707
 
your audience work for <em>something</em></span></a></li><li><a name="footnote-3"><span xmlns="http://www.w3.org/1999/xhtml" class="footnote">We could just go ahead and rename Perspective Broker to be
708
 
Avatar Broker, but 1) that would cause massive compatibility problems, and 2)
709
 
<q>AB</q> doesn't fit into the whole sandwich-themed naming scheme nearly as
710
 
well as <q>PB</q> does. If we changed it to AB, we'd probably have to change
711
 
Banana to be CD (CoderDecoder), and Jelly to be EF (EncapsulatorFragmentor).
712
 
twisted.spread would then have to be renamed twisted.alphabetsoup, and then
713
 
the whole food-pun thing would start all over again.</span></a></li><li><a name="footnote-4"><span xmlns="http://www.w3.org/1999/xhtml" class="footnote">The avatar-ish class is named
714
 
<code>pb.Avatar</code> because <code>pb.Perspective</code> was already
715
 
taken, by the (now obsolete) oldcred perspective-ish class. It is a pity,
716
 
but it simply wasn't possible both replace <code>pb.Perspective</code>
717
 
in-place <em>and</em> maintain a reasonable level of
718
 
backwards-compatibility.</span></a></li></ol></div><p><a href="../howto/index.html">Index</a></p><span class="version">Version: 2.0.1</span></body></html>
 
 
b'\\ No newline at end of file'