~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

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

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE html  PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
 
2
  <head>
 
3
<title>Twisted Documentation: Authentication with Perspective Broker</title>
 
4
<link href="stylesheet.css" rel="stylesheet" type="text/css"/>
 
5
  </head>
 
6
 
 
7
  <body bgcolor="white">
 
8
    <h1 class="title">Authentication with Perspective Broker</h1>
 
9
    <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><li><a href="#auto11">Anonymous Clients</a></li></ul><li><a href="#auto12">Using Avatars</a></li><ul><li><a href="#auto13">Avatar Interfaces</a></li><li><a href="#auto14">Logging Out</a></li><li><a href="#auto15">Making Avatars</a></li><li><a href="#auto16">Connecting and Disconnecting</a></li><li><a href="#auto17">Viewable</a></li><li><a href="#auto18">Chat Server with Avatars</a></li></ul></ol></div>
 
10
    <div class="content">
 
11
<span/>
 
12
 
 
13
<h2>Overview<a name="auto0"/></h2>
 
14
 
 
15
<p>The examples shown in <a href="pb-usage.html" shape="rect">Using Perspective
 
16
Broker</a> demonstrate how to do basic remote method calls, but provided no
 
17
facilities for authentication. In this context, authentication is about who
 
18
gets which remote references, and how to restrict access to the <q>right</q>
 
19
set of people or programs.</p>
 
20
 
 
21
<p>As soon as you have a program which offers services to multiple users,
 
22
where those users should not be allowed to interfere with each other, you
 
23
need to think about authentication. Many services use the idea of an
 
24
<q>account</q>, and rely upon fact that each user has access to only one
 
25
account. Twisted uses a system called <a href="cred.html" shape="rect">cred</a> to
 
26
handle authentication issues, and Perspective Broker has code to make it
 
27
easy to implement the most common use cases.</p>
 
28
 
 
29
<h2>Compartmentalizing Services<a name="auto1"/></h2>
 
30
 
 
31
<p>Imagine how you would write a chat server using PB. The first step might
 
32
be a <code>ChatServer</code> object which had a bunch of
 
33
<code>pb.RemoteReference</code>s that point at user clients. Pretend that
 
34
those clients offered a <code>remote_print</code> method which lets the
 
35
server print a message on the user's console. In that case, the server might
 
36
look something like this:</p>
 
37
 
 
38
<pre class="python"><p class="py-linenumber"> 1
 
39
 2
 
40
 3
 
41
 4
 
42
 5
 
43
 6
 
44
 7
 
45
 8
 
46
 9
 
47
10
 
48
11
 
49
12
 
50
13
 
51
14
 
52
15
 
53
16
 
54
17
 
55
</p><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>):
 
56
 
 
57
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
 
58
        <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span> = {} <span class="py-src-comment"># indexed by name</span>
 
59
        <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span> = {} <span class="py-src-comment"># indexed by name</span>
 
60
    <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>):
 
61
        <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>):
 
62
            <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>[<span class="py-src-variable">groupname</span>] = []
 
63
        <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>])
 
64
    <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>):
 
65
        <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>]
 
66
        <span class="py-src-keyword">if</span> <span class="py-src-variable">group</span>:
 
67
            <span class="py-src-comment"># send the message to all members of the group</span>
 
68
            <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>:
 
69
                <span class="py-src-variable">user</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;print&quot;</span>,
 
70
                                <span class="py-src-string">&quot;&lt;%s&gt; says: %s&quot;</span> % (<span class="py-src-variable">from_username</span>,
 
71
                                                         <span class="py-src-variable">message</span>))
 
72
</pre>
 
73
 
 
74
<p>For now, assume that all clients have somehow acquired a
 
75
<code>pb.RemoteReference</code> to this <code>ChatServer</code> object,
 
76
perhaps using <code>pb.Root</code> and <code>getRootObject</code> as
 
77
described in the <a href="pb-usage.html" shape="rect">previous chapter</a>. In this
 
78
scheme, when a user sends a message to the group, their client runs
 
79
something like the following:</p>
 
80
 
 
81
<pre class="python"><p class="py-linenumber">1
 
82
</p><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>)
 
83
</pre>
 
84
 
 
85
 
 
86
<h3>Incorrect Arguments<a name="auto2"/></h3>
 
87
 
 
88
<p>You've probably seen the first problem: users can trivially spoof each
 
89
other. We depend upon the user to pass a correct value in their
 
90
<q>username</q> argument, and have no way to tell if they're lying or not.
 
91
There is nothing to prevent Alice from modifying her client to do:</p>
 
92
 
 
93
<pre class="python"><p class="py-linenumber">1
 
94
</p><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>)
 
95
</pre>
 
96
 
 
97
<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>
 
98
 
 
99
<p>(In general, learn to get suspicious if you see any argument of a
 
100
remotely-invokable method described as <q>must be X</q>)</p>
 
101
 
 
102
<p>The best way to fix this is to keep track of the user's name locally,
 
103
rather than asking them to send it to the server with each message. The best
 
104
place to keep state is in an object, so this suggests we need a per-user
 
105
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
 
106
<code>User</code> class.
 
107
</p>
 
108
 
 
109
<pre class="python"><p class="py-linenumber"> 1
 
110
 2
 
111
 3
 
112
 4
 
113
 5
 
114
 6
 
115
 7
 
116
 8
 
117
 9
 
118
10
 
119
11
 
120
12
 
121
13
 
122
14
 
123
15
 
124
16
 
125
17
 
126
18
 
127
19
 
128
20
 
129
21
 
130
22
 
131
23
 
132
24
 
133
25
 
134
</p><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>):
 
135
    <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>):
 
136
        <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">username</span>
 
137
        <span class="py-src-variable">self</span>.<span class="py-src-variable">server</span> = <span class="py-src-variable">server</span>
 
138
        <span class="py-src-variable">self</span>.<span class="py-src-variable">remote</span> = <span class="py-src-variable">clientref</span>
 
139
    <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>):
 
140
        <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>)
 
141
    <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>):
 
142
        <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>)
 
143
    <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>):
 
144
        <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>)
 
145
 
 
146
<span class="py-src-keyword">class</span> <span class="py-src-identifier">ChatServer</span>:
 
147
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
 
148
        <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span> = {} <span class="py-src-comment"># indexed by name</span>
 
149
    <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>):
 
150
        <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>):
 
151
            <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>[<span class="py-src-variable">groupname</span>] = []
 
152
        <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>)
 
153
    <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>):
 
154
        <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>]
 
155
        <span class="py-src-keyword">if</span> <span class="py-src-variable">group</span>:
 
156
            <span class="py-src-comment"># send the message to all members of the group</span>
 
157
            <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>:
 
158
                <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>))
 
159
</pre>
 
160
 
 
161
<p>Again, assume that each remote client gets access to a single
 
162
<code>User</code> object, which is created with the proper username.</p>
 
163
 
 
164
<p>Note how the <code>ChatServer</code> object has no remote access: it
 
165
isn't even <code>pb.Referenceable</code> anymore. This means that all access
 
166
to it must be mediated through other objects, with code that is under your
 
167
control.</p>
 
168
 
 
169
<p>As long as Alice only has access to her own <code>User</code> object, she
 
170
can no longer spoof Bob. The only way for her to invoke
 
171
<code>ChatServer.sendMessage</code> is to call her <code>User</code>
 
172
object's <code>remote_sendMessage</code> method, and that method uses its
 
173
own state to provide the <code>from_username</code> argument. It doesn't
 
174
give her any way to change that state.</p>
 
175
 
 
176
<p>This restriction is important. The <code>User</code> object is able to
 
177
maintain its own integrity because there is a wall between the object and
 
178
the client: the client cannot inspect or modify internal state, like the
 
179
<code>.name</code> attribute. The only way through this wall is via remote
 
180
method invocations, and the only control Alice has over those invocations is
 
181
when they get invoked and what arguments they are given.</p>
 
182
 
 
183
<div class="note"><strong>Note: </strong>
 
184
<p>No object can maintain its integrity against local threats: by design,
 
185
Python offers no mechanism for class instances to hide their attributes, and
 
186
once an intruder has a copy of <code>self.__dict__</code>, they can do
 
187
everything the original object was able to do.</p>
 
188
</div>
 
189
 
 
190
 
 
191
<h3>Unforgeable References<a name="auto3"/></h3>
 
192
 
 
193
<p>Now suppose you wanted to implement group parameters, for example a mode
 
194
in which nobody was allowed to talk about mattresses because some users were
 
195
sensitive and calming them down after someone said <q>mattress</q> is a
 
196
hassle that were best avoided altogether. Again, per-group state implies a
 
197
per-group object. We'll go out on a limb and call this the
 
198
<code>Group</code> object:</p>
 
199
 
 
200
<pre class="python"><p class="py-linenumber"> 1
 
201
 2
 
202
 3
 
203
 4
 
204
 5
 
205
 6
 
206
 7
 
207
 8
 
208
 9
 
209
10
 
210
11
 
211
12
 
212
13
 
213
14
 
214
15
 
215
16
 
216
17
 
217
18
 
218
19
 
219
20
 
220
21
 
221
22
 
222
23
 
223
24
 
224
25
 
225
26
 
226
27
 
227
28
 
228
29
 
229
30
 
230
31
 
231
</p><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>):
 
232
    <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>):
 
233
        <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">username</span>
 
234
        <span class="py-src-variable">self</span>.<span class="py-src-variable">server</span> = <span class="py-src-variable">server</span>
 
235
        <span class="py-src-variable">self</span>.<span class="py-src-variable">remote</span> = <span class="py-src-variable">clientref</span>
 
236
    <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>):
 
237
        <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>)
 
238
    <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>):
 
239
        <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>)
 
240
 
 
241
<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>):
 
242
    <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>):
 
243
        <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">groupname</span>
 
244
        <span class="py-src-variable">self</span>.<span class="py-src-variable">allowMattress</span> = <span class="py-src-variable">allowMattress</span>
 
245
        <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span> = []
 
246
    <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>):
 
247
        <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>:
 
248
            <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>
 
249
        <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>:
 
250
            <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>))
 
251
    <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>):
 
252
        <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>)
 
253
 
 
254
<span class="py-src-keyword">class</span> <span class="py-src-identifier">ChatServer</span>:
 
255
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
 
256
        <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span> = {} <span class="py-src-comment"># indexed by name</span>
 
257
    <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>):
 
258
        <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>):
 
259
            <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>)
 
260
        <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>)
 
261
        <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>]
 
262
</pre>
 
263
 
 
264
 
 
265
<p>This example takes advantage of the fact that
 
266
<code>pb.Referenceable</code> objects sent over a wire can be returned to
 
267
you, and they will be turned into references to the same object that you
 
268
originally sent. The client cannot modify the object in any way: all they
 
269
can do is point at it and invoke its <code>remote_*</code> methods. Thus,
 
270
you can be sure that the <code>.name</code> attribute remains the same as
 
271
you left it. In this case, the client code would look something like
 
272
this:</p>
 
273
 
 
274
<pre class="python"><p class="py-linenumber">1
 
275
2
 
276
3
 
277
4
 
278
5
 
279
6
 
280
7
 
281
8
 
282
9
 
283
</p><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>):
 
284
    <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>):
 
285
        <span class="py-src-keyword">print</span> <span class="py-src-variable">message</span>
 
286
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">join</span>(<span class="py-src-parameter">self</span>):
 
287
        <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>,
 
288
                                       <span class="py-src-variable">allowMattress</span>=<span class="py-src-variable">False</span>)
 
289
        <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>)
 
290
    <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>):
 
291
        <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>)
 
292
</pre>
 
293
 
 
294
<p>The <code>User</code> object is sent from the server side, and is turned
 
295
into a <code>pb.RemoteReference</code> when it arrives at the client. The
 
296
client sends it back to <code>Group.remote_send</code>, and PB turns it back
 
297
into a reference to the original <code>User</code> when it gets there.
 
298
<code>Group.remote_send</code> can then use its <code>.name</code> attribute
 
299
as the sender of the message.</p>
 
300
 
 
301
<div class="note"><strong>Note: </strong>
 
302
 
 
303
<p>Third party references (there aren't any)</p>
 
304
 
 
305
<p>This technique also relies upon the fact that the
 
306
<code>pb.Referenceable</code> reference can <em>only</em> come from someone
 
307
who holds a corresponding <code>pb.RemoteReference</code>. The design of the
 
308
serialization mechanism (implemented in <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.jelly.html" title="twisted.spread.jelly">twisted.spread.jelly</a></code>: pb, jelly, spread.. get it?  Look for
 
309
<q>banana</q>, too.  What other networking framework
 
310
can claim API names based on sandwich ingredients?) makes it impossible for
 
311
a client to obtain a reference that they weren't explicitly given.
 
312
References passed over the wire are given id numbers and recorded in a
 
313
per-connection dictionary. If you didn't give them the reference, the id
 
314
number won't be in the dict, and no amount of guessing by a malicious client
 
315
will give them anything else. The dict goes away when the connection is
 
316
dropped, further limiting the scope of those references.</p>
 
317
 
 
318
<p>Futhermore, it is not possible for Bob to send <em>his</em>
 
319
<code>User</code> reference to Alice (perhaps over some other PB channel
 
320
just between the two of them). Outside the context of Bob's connection to
 
321
the server, that reference is just a meaningless number. To prevent
 
322
confusion, PB will tell you if you try to give it away: when you try to hand
 
323
a <code>pb.RemoteReference</code> to a third party, you'll get an exception
 
324
(implemented with an assert in pb.py:364 RemoteReference.jellyFor).</p>
 
325
 
 
326
<p>This helps the security model somewhat: only the client you gave the
 
327
reference to can cause any damage with it. Of course, the client might be a
 
328
brainless zombie, simply doing anything some third party wants. When it's
 
329
not proxying <code>callRemote</code> invocations, it's probably terrorizing
 
330
the living and searching out human brains for sustenance. In short, if you
 
331
don't trust them, don't give them that reference.</p>
 
332
 
 
333
<p>And remember that everything you've ever given them over that connection
 
334
can come back to you. If expect the client to invoke your method with some
 
335
object A that you sent to them earlier, and instead they send you object B
 
336
(that you also sent to them earlier), and you don't check it somehow, then
 
337
you've just opened up a security hole (we'll see an example of this
 
338
shortly). It may be better to keep such objects in a dictionary on the
 
339
server side, and have the client send you an index string instead. Doing it
 
340
that way makes it obvious that they can send you anything they want, and
 
341
improves the chances that you'll remember to implement the right checks.
 
342
(This is exactly what PB is doing underneath, with a per-connection
 
343
dictionary of <code>Referenceable</code> objects, indexed by a number).</p>
 
344
 
 
345
<p>And, of course, you have to make sure you don't accidentally hand out a
 
346
reference to the wrong object.</p>
 
347
 
 
348
</div>
 
349
 
 
350
 
 
351
<p>But again, note the vulnerability. If Alice holds a
 
352
<code>RemoteReference</code> to <em>any</em> object on the server side that
 
353
has a <code>.name</code> attribute, she can use that name as a spoofed
 
354
<q>from</q> parameter. As a simple example, what if her client code looked
 
355
like:</p>
 
356
 
 
357
<pre class="python"><p class="py-linenumber">1
 
358
2
 
359
3
 
360
4
 
361
5
 
362
6
 
363
</p><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>):
 
364
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">join</span>(<span class="py-src-parameter">self</span>):
 
365
        <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>)
 
366
        <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>)
 
367
    <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>):
 
368
        <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>)
 
369
</pre>
 
370
 
 
371
<p>This would let her send a message that appeared to come from
 
372
<q>#twisted</q> rather than <q>Alice</q>. If she joined a group that
 
373
happened to be named <q>bob</q> (perhaps it is the <q>How To Be Bob</q>
 
374
channel, populated by Alice and countless others, a place where they can
 
375
share stories about their best impersonating-Bob moments), then she would be
 
376
able to emit a message that looked like <q>&lt;bob&gt; says: hi there</q>,
 
377
and she has accomplished her lifelong goal.</p>
 
378
 
 
379
 
 
380
<h3>Argument Typechecking<a name="auto4"/></h3>
 
381
 
 
382
<p>There are two techniques to close this hole. The first is to have your
 
383
remotely-invokable methods do type-checking on their arguments: if
 
384
<code>Group.remote_send</code> asserted <code>isinstance(from_user,
 
385
User)</code> then Alice couldn't use non-User objects to do her spoofing,
 
386
and hopefully the rest of the system is designed well enough to prevent her
 
387
from obtaining access to somebody else's User object.</p>
 
388
 
 
389
 
 
390
<h3>Objects as Capabilities<a name="auto5"/></h3>
 
391
 
 
392
<p>The second technique is to avoid having the client send you the objects
 
393
altogether. If they don't send you anything, there is nothing to verify. In
 
394
this case, you would have to have a per-user-per-group object, in which the
 
395
<code>remote_send</code> method would only take a single
 
396
<code>message</code> argument. The <code>UserGroup</code> object is created
 
397
with references to the only <code>User</code> and <code>Group</code> objects
 
398
that it will ever use, so no lookups are needed:</p>
 
399
 
 
400
<pre class="python"><p class="py-linenumber"> 1
 
401
 2
 
402
 3
 
403
 4
 
404
 5
 
405
 6
 
406
 7
 
407
 8
 
408
 9
 
409
10
 
410
11
 
411
12
 
412
13
 
413
14
 
414
15
 
415
16
 
416
17
 
417
18
 
418
19
 
419
</p><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>):
 
420
    <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>):
 
421
        <span class="py-src-variable">self</span>.<span class="py-src-variable">user</span> = <span class="py-src-variable">user</span>
 
422
        <span class="py-src-variable">self</span>.<span class="py-src-variable">group</span> = <span class="py-src-variable">group</span>
 
423
    <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>):
 
424
        <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>)
 
425
 
 
426
<span class="py-src-keyword">class</span> <span class="py-src-identifier">Group</span>:
 
427
    <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>):
 
428
        <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">groupname</span>
 
429
        <span class="py-src-variable">self</span>.<span class="py-src-variable">allowMattress</span> = <span class="py-src-variable">allowMattress</span>
 
430
        <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span> = []
 
431
    <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>):
 
432
        <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>:
 
433
            <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>
 
434
        <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>:
 
435
            <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>))
 
436
    <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>):
 
437
        <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>)
 
438
</pre>
 
439
 
 
440
<p>The only message-sending method Alice has left is
 
441
<code>UserGroup.remote_send</code>, and it only accepts a message: there are
 
442
no remaining ways to influence the <q>from</q> name.</p>
 
443
 
 
444
<p>In this model, each remotely-accessible object represents a very small
 
445
set of capabilities. Security is achieved by only granting a minimal set of
 
446
abilities to each remote user.</p>
 
447
 
 
448
<p>PB provides a shortcut which makes this technique easier to use. The
 
449
<code>Viewable</code> class will be discussed <a href="#viewable" shape="rect">below</a>.</p>
 
450
 
 
451
<h2>Avatars and Perspectives<a name="auto6"/></h2>
 
452
 
 
453
<p>In Twisted's <a href="cred.html" shape="rect">cred</a> system, an <q>Avatar</q> is
 
454
an object that lives on the <q>server</q> side (defined here as the side
 
455
farthest from the human who is trying to get something done) which lets the
 
456
remote user get something done. The avatar isn't really a particular class,
 
457
it's more like a description of a role that some object plays, as in <q>the
 
458
Foo object here is acting as the user's avatar for this particular
 
459
service</q>. Generally, the remote user has some way of getting their avatar
 
460
to run some code. The avatar object may enforce some security checks, and
 
461
provide additional data, then call other methods which get things done.</p>
 
462
 
 
463
<p>The two pieces in the cred puzzle (for any protocol, not just PB) are:
 
464
<q>what serves as the Avatar?</q>, and <q>how does the user get access to
 
465
it?</q>.</p>
 
466
 
 
467
<p>For PB, the first question is easy. The Avatar is a remotely-accessible
 
468
object which can run code: this is a perfect description of
 
469
<code>pb.Referenceable</code> and its subclasses. We shall defer the second
 
470
question until the next section.</p>
 
471
 
 
472
<p>In the example above, you can think of the <code>ChatServer</code> and
 
473
<code>Group</code> objects as a service. The <code>User</code> object is the
 
474
user's server-side representative: everything the user is capable of doing
 
475
is done by running one of its methods. Anything that the server wants to do
 
476
to the user (change their group membership, change their name, delete their
 
477
pet cat, whatever) is done by manipulating the <code>User</code> object.</p>
 
478
 
 
479
<p>There are multiple User objects living in peace and harmony around the
 
480
ChatServer. Each has a different point of view on the services provided by
 
481
the ChatServer and the Groups: each may belong to different groups, some
 
482
might have more permissions than others (like the ability to create groups).
 
483
These different points of view are called <q>Perspectives</q>. This is the
 
484
origin of the term <q>Perspective</q> in <q>Perspective Broker</q>: PB
 
485
provides and controls (i.e. <q>brokers</q>) access to Perspectives.</p>
 
486
 
 
487
<p>Once upon a time, these local-representative objects were actually called
 
488
<code>pb.Perspective</code>. But this has changed with the advent of the
 
489
rewritten cred system, and now the more generic term for a local
 
490
representative object is an Avatar. But you will still see reference to
 
491
<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
 
492
that perspectives and avatars are basically the same thing. </p>
 
493
 
 
494
<p>Despite all we've been <a href="cred.html" shape="rect">telling you</a> about how
 
495
Avatars are more of a concept than an actual class, the base class from
 
496
which you can create your server-side avatar-ish objects is, in fact, named
 
497
<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
 
498
<code>pb.Referenceable</code>. The only difference is that instead of
 
499
offering <q>remote_FOO</q> methods, they offer <q>perspective_FOO</q>
 
500
methods.</p>
 
501
 
 
502
<p>The other way in which <code>pb.Avatar</code> differs from
 
503
<code>pb.Referenceable</code> is that the avatar objects are designed to be
 
504
the first thing retrieved by a cred-using remote client. Just as
 
505
<code>PBClientFactory.getRootObject</code> gives the client access to a
 
506
<code>pb.Root</code> object (which can then provide access to all kinds of
 
507
other objects), <code>PBClientFactory.login</code> gives client access to a
 
508
<code>pb.Avatar</code> object (which can return other references). </p>
 
509
 
 
510
<p>So, the first half of using cred in your PB application is to create an
 
511
Avatar object which implements <code>perspective_</code> methods and is
 
512
careful to do useful things for the remote user while remaining vigilant
 
513
against being tricked with unexpected argument values. It must also be
 
514
careful to never give access to objects that the user should not have access
 
515
to, whether by returning them directly, returning objects which contain
 
516
them, or returning objects which can be asked (remotely) to provide
 
517
them.</p>
 
518
 
 
519
<p>The second half is how the user gets a <code>pb.RemoteReference</code> to
 
520
your Avatar. As explained <a href="cred.html" shape="rect">elsewhere</a>, Avatars are
 
521
obtained from a Realm. The Realm doesn't deal with authentication at all
 
522
(usernames, passwords, public keys, challenge-response systems, retinal
 
523
scanners, real-time DNA sequencers, etc). It simply takes an <q>avatarID</q>
 
524
(which is effectively a username) and returns an Avatar object. The Portal
 
525
and its Checkers deal with authenticating the user: by the time they are
 
526
done, the remote user has proved their right to access the avatarID that is
 
527
given to the Realm, so the Realm can return a remotely-controllable object
 
528
that has whatever powers you wish to grant to this particular user. </p>
 
529
 
 
530
<p>For PB, the realm is expected to return a <code>pb.Avatar</code> (or
 
531
anything which implements <code>pb.IPerspective</code>, really, but there's
 
532
no reason to not return a <code>pb.Avatar</code> subclass). This object will
 
533
be given to the client just like a <code>pb.Root</code> would be without
 
534
cred, and the user can get access to other objects through it (if you let
 
535
them).</p>
 
536
 
 
537
<p>The basic idea is that there is a separate IPerspective-implementing
 
538
object (i.e. the Avatar subclass) (i.e. the <q>perspective</q>) for each
 
539
user, and <em>only</em> the authorized user gets a remote reference to that
 
540
object. You can store whatever permissions or capabilities the user
 
541
possesses in that object, and then use them when the user invokes a remote
 
542
method. You give the user access to the perspective object instead of the
 
543
objects that do the real work.</p>
 
544
 
 
545
 
 
546
<h2>Perspective Examples<a name="auto7"/></h2>
 
547
 
 
548
<p>Here is a brief example of using a pb.Avatar. Most of the support code
 
549
is magic for now: we'll explain it later.</p>
 
550
 
 
551
<h3>One Client<a name="auto8"/></h3>
 
552
 
 
553
<div class="py-listing"><pre><p class="py-linenumber"> 1
 
554
 2
 
555
 3
 
556
 4
 
557
 5
 
558
 6
 
559
 7
 
560
 8
 
561
 9
 
562
10
 
563
11
 
564
12
 
565
13
 
566
14
 
567
15
 
568
16
 
569
17
 
570
18
 
571
19
 
572
20
 
573
21
 
574
22
 
575
23
 
576
24
 
577
25
 
578
26
 
579
27
 
580
28
 
581
29
 
582
</p><span class="py-src-comment">#!/usr/bin/env python</span>
 
583
 
 
584
<span class="py-src-comment"># Copyright (c) 2009 Twisted Matrix Laboratories.</span>
 
585
<span class="py-src-comment"># See LICENSE for details.</span>
 
586
 
 
587
<span class="py-src-keyword">from</span> <span class="py-src-variable">zope</span>.<span class="py-src-variable">interface</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">implements</span>
 
588
 
 
589
<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>
 
590
<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>
 
591
<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>
 
592
 
 
593
<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>):
 
594
    <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>):
 
595
        <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span>
 
596
    <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>):
 
597
        <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>
 
598
 
 
599
<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyRealm</span>:
 
600
    <span class="py-src-variable">implements</span>(<span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>)
 
601
    <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>):
 
602
        <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>:
 
603
            <span class="py-src-keyword">raise</span> <span class="py-src-variable">NotImplementedError</span>
 
604
        <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>
 
605
 
 
606
<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>())
 
607
<span class="py-src-variable">p</span>.<span class="py-src-variable">registerChecker</span>(
 
608
    <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>))
 
609
<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>))
 
610
<span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
 
611
</pre><div class="caption">Source listing - <a href="listings/pb/pb5server.py"><span class="filename">listings/pb/pb5server.py</span></a></div></div>
 
612
<div class="py-listing"><pre><p class="py-linenumber"> 1
 
613
 2
 
614
 3
 
615
 4
 
616
 5
 
617
 6
 
618
 7
 
619
 8
 
620
 9
 
621
10
 
622
11
 
623
12
 
624
13
 
625
14
 
626
15
 
627
16
 
628
17
 
629
18
 
630
19
 
631
20
 
632
21
 
633
22
 
634
</p><span class="py-src-comment">#!/usr/bin/env python</span>
 
635
 
 
636
<span class="py-src-comment"># Copyright (c) 2009 Twisted Matrix Laboratories.</span>
 
637
<span class="py-src-comment"># See LICENSE for details.</span>
 
638
 
 
639
<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>
 
640
<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>
 
641
<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>
 
642
 
 
643
<span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
 
644
    <span class="py-src-variable">factory</span> = <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBClientFactory</span>()
 
645
    <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>)
 
646
    <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>))
 
647
    <span class="py-src-variable">def1</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">connected</span>)
 
648
    <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
 
649
 
 
650
<span class="py-src-keyword">def</span> <span class="py-src-identifier">connected</span>(<span class="py-src-parameter">perspective</span>):
 
651
    <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>
 
652
    <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;asking it to foo(12)&quot;</span>
 
653
    <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>)
 
654
 
 
655
<span class="py-src-variable">main</span>()
 
656
</pre><div class="caption">Source listing - <a href="listings/pb/pb5client.py"><span class="filename">listings/pb/pb5client.py</span></a></div></div>
 
657
 
 
658
<p>Ok, so that wasn't really very exciting. It doesn't accomplish much more
 
659
than the first PB example, and used a lot more code to do it. Let's try it
 
660
again with two users this time.</p>
 
661
 
 
662
<div class="note"><strong>Note: </strong>
 
663
 
 
664
<p>When the client runs <code>login</code> to request the Perspective,
 
665
they can provide it with an optional <code>client</code> argument (which
 
666
must be a <code>pb.Referenceable</code> object). If they do, then a
 
667
reference to that object will be handed to the realm's
 
668
<code>requestAvatar</code> in the <code>mind</code> argument.</p>
 
669
 
 
670
<p>The server-side Perspective can use it to invoke remote methods on
 
671
something in the client, so that the client doesn't always have to drive the
 
672
interaction. In a chat server, the client object would be the one to which
 
673
<q>display text</q> messages were sent. In a board game server, this would
 
674
provide a way to tell the clients that someone has made a move, so they can
 
675
update their game boards.</p>
 
676
 
 
677
</div>
 
678
 
 
679
<h3>Two Clients<a name="auto9"/></h3>
 
680
 
 
681
<div class="py-listing"><pre><p class="py-linenumber"> 1
 
682
 2
 
683
 3
 
684
 4
 
685
 5
 
686
 6
 
687
 7
 
688
 8
 
689
 9
 
690
10
 
691
11
 
692
12
 
693
13
 
694
14
 
695
15
 
696
16
 
697
17
 
698
18
 
699
19
 
700
20
 
701
21
 
702
22
 
703
23
 
704
24
 
705
25
 
706
26
 
707
27
 
708
28
 
709
29
 
710
30
 
711
</p><span class="py-src-comment">#!/usr/bin/env python</span>
 
712
 
 
713
<span class="py-src-comment"># Copyright (c) 2009 Twisted Matrix Laboratories.</span>
 
714
<span class="py-src-comment"># See LICENSE for details.</span>
 
715
 
 
716
<span class="py-src-keyword">from</span> <span class="py-src-variable">zope</span>.<span class="py-src-variable">interface</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">implements</span>
 
717
 
 
718
<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>
 
719
<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>
 
720
<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>
 
721
 
 
722
<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>):
 
723
    <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>):
 
724
        <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span>
 
725
    <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>):
 
726
        <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>
 
727
 
 
728
<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyRealm</span>:
 
729
    <span class="py-src-variable">implements</span>(<span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>)
 
730
    <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>):
 
731
        <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>:
 
732
            <span class="py-src-keyword">raise</span> <span class="py-src-variable">NotImplementedError</span>
 
733
        <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>
 
734
 
 
735
<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>())
 
736
<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>,
 
737
                                                     <span class="py-src-variable">user2</span>=<span class="py-src-string">&quot;pass2&quot;</span>)
 
738
<span class="py-src-variable">p</span>.<span class="py-src-variable">registerChecker</span>(<span class="py-src-variable">c</span>)
 
739
<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>))
 
740
<span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
 
741
</pre><div class="caption">Source listing - <a href="listings/pb/pb6server.py"><span class="filename">listings/pb/pb6server.py</span></a></div></div>
 
742
<div class="py-listing"><pre><p class="py-linenumber"> 1
 
743
 2
 
744
 3
 
745
 4
 
746
 5
 
747
 6
 
748
 7
 
749
 8
 
750
 9
 
751
10
 
752
11
 
753
12
 
754
13
 
755
14
 
756
15
 
757
16
 
758
17
 
759
18
 
760
19
 
761
20
 
762
21
 
763
22
 
764
</p><span class="py-src-comment">#!/usr/bin/env python</span>
 
765
 
 
766
<span class="py-src-comment"># Copyright (c) 2009 Twisted Matrix Laboratories.</span>
 
767
<span class="py-src-comment"># See LICENSE for details.</span>
 
768
 
 
769
<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>
 
770
<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>
 
771
<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>
 
772
 
 
773
<span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
 
774
    <span class="py-src-variable">factory</span> = <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBClientFactory</span>()
 
775
    <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>)
 
776
    <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>))
 
777
    <span class="py-src-variable">def1</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">connected</span>)
 
778
    <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
 
779
 
 
780
<span class="py-src-keyword">def</span> <span class="py-src-identifier">connected</span>(<span class="py-src-parameter">perspective</span>):
 
781
    <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>
 
782
    <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;asking it to foo(13)&quot;</span>
 
783
    <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>)
 
784
 
 
785
<span class="py-src-variable">main</span>()
 
786
</pre><div class="caption">Source listing - <a href="listings/pb/pb6client1.py"><span class="filename">listings/pb/pb6client1.py</span></a></div></div>
 
787
<div class="py-listing"><pre><p class="py-linenumber"> 1
 
788
 2
 
789
 3
 
790
 4
 
791
 5
 
792
 6
 
793
 7
 
794
 8
 
795
 9
 
796
10
 
797
11
 
798
12
 
799
13
 
800
14
 
801
15
 
802
16
 
803
17
 
804
18
 
805
19
 
806
20
 
807
21
 
808
22
 
809
23
 
810
24
 
811
25
 
812
</p><span class="py-src-comment">#!/usr/bin/env python</span>
 
813
 
 
814
<span class="py-src-comment"># Copyright (c) 2009 Twisted Matrix Laboratories.</span>
 
815
<span class="py-src-comment"># See LICENSE for details.</span>
 
816
 
 
817
<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>
 
818
<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>
 
819
 
 
820
<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>
 
821
<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>
 
822
<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>
 
823
 
 
824
<span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
 
825
    <span class="py-src-variable">factory</span> = <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBClientFactory</span>()
 
826
    <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>)
 
827
    <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>))
 
828
    <span class="py-src-variable">def1</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">connected</span>)
 
829
    <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
 
830
 
 
831
<span class="py-src-keyword">def</span> <span class="py-src-identifier">connected</span>(<span class="py-src-parameter">perspective</span>):
 
832
    <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>
 
833
    <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;asking it to foo(14)&quot;</span>
 
834
    <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>)
 
835
 
 
836
<span class="py-src-variable">main</span>()
 
837
</pre><div class="caption">Source listing - <a href="listings/pb/pb6client2.py"><span class="filename">listings/pb/pb6client2.py</span></a></div></div>
 
838
 
 
839
<p>While pb6server.py is running, try starting pb6client1, then pb6client2.
 
840
Compare the argument passed by the <code>.callRemote()</code> in each
 
841
client. You can see how each client gets connected to a different
 
842
Perspective.</p>
 
843
 
 
844
 
 
845
<h3>How that example worked<a name="auto10"/></h3><a name="smallexample" shape="rect"/>
 
846
 
 
847
<p>Let's walk through the previous example and see what was going on.</p>
 
848
 
 
849
<p>First, we created a subclass called <code>MyPerspective</code> which is
 
850
our server-side Avatar. It implements a <code>perspective_foo</code> method
 
851
that is exposed to the remote client.</p>
 
852
 
 
853
<p>Second, we created a realm (an object which implements
 
854
<code>IRealm</code>, and therefore implements <code>requestAvatar</code>).
 
855
This realm manufactures <code>MyPerspective</code> objects. It makes as many
 
856
as we want, and names each one with the avatarID (a username) that comes out
 
857
of the checkers. This MyRealm object returns two other objects as well,
 
858
which we will describe later.</p>
 
859
 
 
860
<p>Third, we created a portal to hold this realm. The portal's job is to
 
861
dispatch incoming clients to the credential checkers, and then to request
 
862
Avatars for any which survive the authentication process.</p>
 
863
 
 
864
<p>Fourth, we made a simple checker (an object which implements
 
865
<code>IChecker</code>) to hold valid user/password pairs. The checker
 
866
gets registered with the portal, so it knows who to ask when new
 
867
clients connect.  We use a checker named
 
868
<code>InMemoryUsernamePasswordDatabaseDontUse</code>, which suggests
 
869
that 1: all the username/password pairs are kept in memory instead of
 
870
being saved to a database or something, and 2: you shouldn't use
 
871
it. The admonition against using it is because there are better
 
872
schemes: keeping everything in memory will not work when you have
 
873
thousands or millions of users to keep track of, the passwords will be
 
874
stored in the .tap file when the application shuts down (possibly a
 
875
security risk), and finally it is a nuisance to add or remove users
 
876
after the checker is constructed.</p>
 
877
 
 
878
<p>Fifth, we create a <code>pb.PBServerFactory</code> to listen on a TCP
 
879
port. This factory knows how to connect the remote client to the Portal, so
 
880
incoming connections will be handed to the authentication process. Other
 
881
protocols (non-PB) would do something similar: the factory that creates
 
882
Protocol objects will give those objects access to the Portal so
 
883
authentication can take place.</p>
 
884
 
 
885
<p>On the client side, a <code>pb.PBClientFactory</code> is created (as <a href="pb-usage.html" shape="rect">before</a>) and attached to a TCP connection. When the
 
886
connection completes, the factory will be asked to produce a Protocol, and
 
887
it will create a PB object. Unlike the previous chapter, where we used
 
888
<code>.getRootObject</code>, here we use <code>factory.login</code> to
 
889
initiate the cred authentication process. We provide a
 
890
<code>credentials</code> object, which is the client-side agent for doing
 
891
our half of the authentication process. This process may involve several
 
892
messages: challenges, responses, encrypted passwords, secure hashes, etc. We
 
893
give our credentials object everything it will need to respond correctly (in
 
894
this case, a username and password, but you could write a credential that
 
895
used public-key encryption or even fancier techniques).</p>
 
896
 
 
897
<p><code>login</code> returns a Deferred which, when it fires, will return a
 
898
<code>pb.RemoteReference</code> to the remote avatar. We can then do
 
899
<code>callRemote</code> to invoke a <code>perspective_foo</code> method on
 
900
that Avatar.</p>
 
901
 
 
902
 
 
903
<h3>Anonymous Clients<a name="auto11"/></h3>
 
904
 
 
905
<div class="py-listing"><pre><p class="py-linenumber"> 1
 
906
 2
 
907
 3
 
908
 4
 
909
 5
 
910
 6
 
911
 7
 
912
 8
 
913
 9
 
914
10
 
915
11
 
916
12
 
917
13
 
918
14
 
919
15
 
920
16
 
921
17
 
922
18
 
923
19
 
924
20
 
925
21
 
926
22
 
927
23
 
928
24
 
929
25
 
930
26
 
931
27
 
932
28
 
933
29
 
934
30
 
935
31
 
936
32
 
937
33
 
938
34
 
939
35
 
940
36
 
941
37
 
942
38
 
943
39
 
944
40
 
945
41
 
946
42
 
947
43
 
948
44
 
949
45
 
950
46
 
951
47
 
952
48
 
953
49
 
954
50
 
955
51
 
956
52
 
957
53
 
958
54
 
959
55
 
960
56
 
961
57
 
962
58
 
963
59
 
964
60
 
965
61
 
966
62
 
967
63
 
968
64
 
969
65
 
970
66
 
971
67
 
972
68
 
973
69
 
974
70
 
975
71
 
976
72
 
977
73
 
978
74
 
979
75
 
980
76
 
981
77
 
982
78
 
983
79
 
984
80
 
985
81
 
986
82
 
987
83
 
988
84
 
989
85
 
990
86
 
991
87
 
992
88
 
993
89
 
994
90
 
995
91
 
996
</p><span class="py-src-comment">#!/usr/bin/env python</span>
 
997
 
 
998
<span class="py-src-comment"># Copyright (c) 2007-2009 Twisted Matrix Laboratories.</span>
 
999
<span class="py-src-comment"># See LICENSE for details.</span>
 
1000
 
 
1001
<span class="py-src-string">&quot;&quot;&quot;
 
1002
Implement the realm for and run on port 8800 a PB service which allows both
 
1003
anonymous and username/password based access.
 
1004
 
 
1005
Successful username/password-based login requests given an instance of
 
1006
MyPerspective with a name which matches the username with which they
 
1007
authenticated.  Success anonymous login requests are given an instance of
 
1008
MyPerspective with the name &quot;Anonymous&quot;.
 
1009
&quot;&quot;&quot;</span>
 
1010
 
 
1011
<span class="py-src-keyword">from</span> <span class="py-src-variable">sys</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">stdout</span>
 
1012
 
 
1013
<span class="py-src-keyword">from</span> <span class="py-src-variable">zope</span>.<span class="py-src-variable">interface</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">implements</span>
 
1014
 
 
1015
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">python</span>.<span class="py-src-variable">log</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">startLogging</span>
 
1016
<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-variable">checkers</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">ANONYMOUS</span>, <span class="py-src-variable">AllowAnonymousAccess</span>
 
1017
<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-variable">checkers</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">InMemoryUsernamePasswordDatabaseDontUse</span>
 
1018
<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-variable">portal</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">IRealm</span>, <span class="py-src-variable">Portal</span>
 
1019
<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>
 
1020
<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-variable">pb</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Avatar</span>, <span class="py-src-variable">IPerspective</span>, <span class="py-src-variable">PBServerFactory</span>
 
1021
 
 
1022
 
 
1023
<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyPerspective</span>(<span class="py-src-parameter">Avatar</span>):
 
1024
    <span class="py-src-string">&quot;&quot;&quot;
 
1025
    Trivial avatar exposing a single remote method for demonstrative
 
1026
    purposes.  All successful login attempts in this example will result in
 
1027
    an avatar which is an instance of this class.
 
1028
 
 
1029
    @type name: C{str}
 
1030
    @ivar name: The username which was used during login or C{&quot;Anonymous&quot;}
 
1031
    if the login was anonymous (a real service might want to avoid the
 
1032
    collision this introduces between anonoymous users and authenticated
 
1033
    users named &quot;Anonymous&quot;).
 
1034
    &quot;&quot;&quot;</span>
 
1035
    <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>):
 
1036
        <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span>
 
1037
 
 
1038
 
 
1039
    <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>):
 
1040
        <span class="py-src-string">&quot;&quot;&quot;
 
1041
        Print a simple message which gives the argument this method was
 
1042
        called with and this avatar's name.
 
1043
        &quot;&quot;&quot;</span>
 
1044
        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;I am %s.  perspective_foo(%s) called on %s.&quot;</span> % (
 
1045
            <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span>, <span class="py-src-variable">arg</span>, <span class="py-src-variable">self</span>)
 
1046
 
 
1047
 
 
1048
 
 
1049
<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyRealm</span>(<span class="py-src-parameter">object</span>):
 
1050
    <span class="py-src-string">&quot;&quot;&quot;
 
1051
    Trivial realm which supports anonymous and named users by creating
 
1052
    avatars which are instances of MyPerspective for either.
 
1053
    &quot;&quot;&quot;</span>
 
1054
    <span class="py-src-variable">implements</span>(<span class="py-src-variable">IRealm</span>)
 
1055
 
 
1056
    <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>):
 
1057
        <span class="py-src-keyword">if</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>:
 
1058
            <span class="py-src-keyword">raise</span> <span class="py-src-variable">NotImplementedError</span>(<span class="py-src-string">&quot;MyRealm only handles IPerspective&quot;</span>)
 
1059
        <span class="py-src-keyword">if</span> <span class="py-src-variable">avatarId</span> <span class="py-src-keyword">is</span> <span class="py-src-variable">ANONYMOUS</span>:
 
1060
            <span class="py-src-variable">avatarId</span> = <span class="py-src-string">&quot;Anonymous&quot;</span>
 
1061
        <span class="py-src-keyword">return</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>
 
1062
 
 
1063
 
 
1064
 
 
1065
<span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
 
1066
    <span class="py-src-string">&quot;&quot;&quot;
 
1067
    Create a PB server using MyRealm and run it on port 8800.
 
1068
    &quot;&quot;&quot;</span>
 
1069
    <span class="py-src-variable">startLogging</span>(<span class="py-src-variable">stdout</span>)
 
1070
 
 
1071
    <span class="py-src-variable">p</span> = <span class="py-src-variable">Portal</span>(<span class="py-src-variable">MyRealm</span>())
 
1072
 
 
1073
    <span class="py-src-comment"># Here the username/password checker is registered.</span>
 
1074
    <span class="py-src-variable">c1</span> = <span class="py-src-variable">InMemoryUsernamePasswordDatabaseDontUse</span>(<span class="py-src-variable">user1</span>=<span class="py-src-string">&quot;pass1&quot;</span>, <span class="py-src-variable">user2</span>=<span class="py-src-string">&quot;pass2&quot;</span>)
 
1075
    <span class="py-src-variable">p</span>.<span class="py-src-variable">registerChecker</span>(<span class="py-src-variable">c1</span>)
 
1076
 
 
1077
    <span class="py-src-comment"># Here the anonymous checker is registered.</span>
 
1078
    <span class="py-src-variable">c2</span> = <span class="py-src-variable">AllowAnonymousAccess</span>()
 
1079
    <span class="py-src-variable">p</span>.<span class="py-src-variable">registerChecker</span>(<span class="py-src-variable">c2</span>)
 
1080
 
 
1081
    <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">PBServerFactory</span>(<span class="py-src-variable">p</span>))
 
1082
    <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
 
1083
 
 
1084
 
 
1085
<span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> == <span class="py-src-string">'__main__'</span>:
 
1086
    <span class="py-src-variable">main</span>()
 
1087
</pre><div class="caption">Source listing - <a href="listings/pb/pbAnonServer.py"><span class="filename">listings/pb/pbAnonServer.py</span></a></div></div>
 
1088
<div class="py-listing"><pre><p class="py-linenumber"> 1
 
1089
 2
 
1090
 3
 
1091
 4
 
1092
 5
 
1093
 6
 
1094
 7
 
1095
 8
 
1096
 9
 
1097
10
 
1098
11
 
1099
12
 
1100
13
 
1101
14
 
1102
15
 
1103
16
 
1104
17
 
1105
18
 
1106
19
 
1107
20
 
1108
21
 
1109
22
 
1110
23
 
1111
24
 
1112
25
 
1113
26
 
1114
27
 
1115
28
 
1116
29
 
1117
30
 
1118
31
 
1119
32
 
1120
33
 
1121
34
 
1122
35
 
1123
36
 
1124
37
 
1125
38
 
1126
39
 
1127
40
 
1128
41
 
1129
42
 
1130
43
 
1131
44
 
1132
45
 
1133
46
 
1134
47
 
1135
48
 
1136
49
 
1137
50
 
1138
51
 
1139
52
 
1140
53
 
1141
54
 
1142
55
 
1143
56
 
1144
57
 
1145
58
 
1146
59
 
1147
60
 
1148
61
 
1149
62
 
1150
63
 
1151
64
 
1152
65
 
1153
66
 
1154
67
 
1155
68
 
1156
69
 
1157
70
 
1158
</p><span class="py-src-comment">#!/usr/bin/env python</span>
 
1159
 
 
1160
<span class="py-src-comment"># Copyright (c) 2007-2009 Twisted Matrix Laboratories.</span>
 
1161
<span class="py-src-comment"># See LICENSE for details.</span>
 
1162
 
 
1163
<span class="py-src-string">&quot;&quot;&quot;
 
1164
Client which will talk to the server run by pbAnonServer.py, logging in
 
1165
either anonymously or with username/password credentials.
 
1166
&quot;&quot;&quot;</span>
 
1167
 
 
1168
<span class="py-src-keyword">from</span> <span class="py-src-variable">sys</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">stdout</span>
 
1169
 
 
1170
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">python</span>.<span class="py-src-variable">log</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">err</span>, <span class="py-src-variable">startLogging</span>
 
1171
<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-variable">credentials</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">Anonymous</span>, <span class="py-src-variable">UsernamePassword</span>
 
1172
<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>
 
1173
<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-variable">defer</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">gatherResults</span>
 
1174
<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-variable">pb</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">PBClientFactory</span>
 
1175
 
 
1176
 
 
1177
<span class="py-src-keyword">def</span> <span class="py-src-identifier">error</span>(<span class="py-src-parameter">why</span>, <span class="py-src-parameter">msg</span>):
 
1178
    <span class="py-src-string">&quot;&quot;&quot;
 
1179
    Catch-all errback which simply logs the failure.  This isn't expected to
 
1180
    be invoked in the normal case for this example.
 
1181
    &quot;&quot;&quot;</span>
 
1182
    <span class="py-src-variable">err</span>(<span class="py-src-variable">why</span>, <span class="py-src-variable">msg</span>)
 
1183
 
 
1184
 
 
1185
<span class="py-src-keyword">def</span> <span class="py-src-identifier">connected</span>(<span class="py-src-parameter">perspective</span>):
 
1186
    <span class="py-src-string">&quot;&quot;&quot;
 
1187
    Login callback which invokes the remote &quot;foo&quot; method on the perspective
 
1188
    which the server returned.
 
1189
    &quot;&quot;&quot;</span>
 
1190
    <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>
 
1191
    <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;asking it to foo(13)&quot;</span>
 
1192
    <span class="py-src-keyword">return</span> <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>)
 
1193
 
 
1194
 
 
1195
<span class="py-src-keyword">def</span> <span class="py-src-identifier">finished</span>(<span class="py-src-parameter">ignored</span>):
 
1196
    <span class="py-src-string">&quot;&quot;&quot;
 
1197
    Callback invoked when both logins and method calls have finished to shut
 
1198
    down the reactor so the example exits.
 
1199
    &quot;&quot;&quot;</span>
 
1200
    <span class="py-src-variable">reactor</span>.<span class="py-src-variable">stop</span>()
 
1201
 
 
1202
 
 
1203
<span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
 
1204
    <span class="py-src-string">&quot;&quot;&quot;
 
1205
    Connect to a PB server running on port 8800 on localhost and log in to
 
1206
    it, both anonymously and using a username/password it will recognize.
 
1207
    &quot;&quot;&quot;</span>
 
1208
    <span class="py-src-variable">startLogging</span>(<span class="py-src-variable">stdout</span>)
 
1209
    <span class="py-src-variable">factory</span> = <span class="py-src-variable">PBClientFactory</span>()
 
1210
    <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>)
 
1211
 
 
1212
    <span class="py-src-variable">anonymousLogin</span> = <span class="py-src-variable">factory</span>.<span class="py-src-variable">login</span>(<span class="py-src-variable">Anonymous</span>())
 
1213
    <span class="py-src-variable">anonymousLogin</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">connected</span>)
 
1214
    <span class="py-src-variable">anonymousLogin</span>.<span class="py-src-variable">addErrback</span>(<span class="py-src-variable">error</span>, <span class="py-src-string">&quot;Anonymous login failed&quot;</span>)
 
1215
 
 
1216
    <span class="py-src-variable">usernameLogin</span> = <span class="py-src-variable">factory</span>.<span class="py-src-variable">login</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>))
 
1217
    <span class="py-src-variable">usernameLogin</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">connected</span>)
 
1218
    <span class="py-src-variable">usernameLogin</span>.<span class="py-src-variable">addErrback</span>(<span class="py-src-variable">error</span>, <span class="py-src-string">&quot;Username/password login failed&quot;</span>)
 
1219
 
 
1220
    <span class="py-src-variable">bothDeferreds</span> = <span class="py-src-variable">gatherResults</span>([<span class="py-src-variable">anonymousLogin</span>, <span class="py-src-variable">usernameLogin</span>])
 
1221
    <span class="py-src-variable">bothDeferreds</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">finished</span>)
 
1222
 
 
1223
    <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
 
1224
 
 
1225
 
 
1226
<span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> == <span class="py-src-string">'__main__'</span>:
 
1227
    <span class="py-src-variable">main</span>()
 
1228
</pre><div class="caption">Source listing - <a href="listings/pb/pbAnonClient.py"><span class="filename">listings/pb/pbAnonClient.py</span></a></div></div>
 
1229
 
 
1230
<p>pbAnonServer.py implements a server based on pb6server.py, extending it to
 
1231
permit anonymous logins in addition to authenticated logins. A
 
1232
<code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.cred.checkers.AllowAnonymousAccess.html" title="twisted.cred.checkers.AllowAnonymousAccess">AllowAnonymousAccess</a></code>
 
1233
checker and a <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.cred.checkers.
 
1234
.html" title="twisted.cred.checkers.
 
1235
">
 
1236
InMemoryUsernamePasswordDatabaseDontUse</a></code> checker are registered and the
 
1237
client's choice of credentials object determines which is used to authenticate
 
1238
the login.  In either case, the realm will be called on to create an avatar for
 
1239
the login.  <code>AllowAnonymousAccess</code> always produces an <code>avatarId
 
1240
</code> of <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.cred.checkers.ANONYMOUS.html" title="twisted.cred.checkers.ANONYMOUS">ANONYMOUS</a></code>.</p>
 
1241
 
 
1242
<p>On the client side, the only change is the use of an instance of
 
1243
<code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.cred.credentials.Anonymous.html" title="twisted.cred.credentials.Anonymous">Anonymous</a></code> when calling
 
1244
<code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.PBClientFactory.login.html" title="twisted.spread.pb.PBClientFactory.login">PBClientFactory.login</a></code>.</p>
 
1245
 
 
1246
 
 
1247
<h2>Using Avatars<a name="auto12"/></h2>
 
1248
 
 
1249
 
 
1250
<h3>Avatar Interfaces<a name="auto13"/></h3>
 
1251
 
 
1252
<p>The first element of the 3-tuple returned by <code>requestAvatar</code>
 
1253
indicates which Interface this Avatar implements. For PB avatars, it will
 
1254
always be <code>pb.IPerspective</code>, because that's the only interface
 
1255
these avatars implement.</p>
 
1256
 
 
1257
<p>This element is present because <code>requestAvatar</code> is actually
 
1258
presented with a list of possible Interfaces. The question being posed to
 
1259
the Realm is: <q>do you have an avatar for (avatarID) that can implement one
 
1260
of the following set of Interfaces?</q>. Some portals and checkers might
 
1261
give a list of Interfaces and the Realm could pick; the PB code only knows
 
1262
how to do one, so we cannot take advantage of this feature.</p>
 
1263
 
 
1264
<h3>Logging Out<a name="auto14"/></h3>
 
1265
 
 
1266
<p>The third element of the 3-tuple is a zero-argument callable, which will
 
1267
be invoked by the protocol when the connection has been lost. We can use
 
1268
this to notify the Avatar when the client has lost its connection. This will
 
1269
be described in more detail below.</p>
 
1270
 
 
1271
<h3>Making Avatars<a name="auto15"/></h3>
 
1272
 
 
1273
<p>In the example above, we create Avatars upon request, during
 
1274
<code>requestAvatar</code>. Depending upon the service, these Avatars might
 
1275
already exist before the connection is received, and might outlive the
 
1276
connection. The Avatars might also accept multiple connections.</p>
 
1277
 
 
1278
<p>Another possibility is that the Avatars might exist ahead of time, but in
 
1279
a different form (frozen in a pickle and/or saved in a database). In this
 
1280
case, <code>requestAvatar</code> may need to perform a database lookup and
 
1281
then do something with the result before it can provide an avatar. In this
 
1282
case, it would probably return a Deferred so it could provide the real
 
1283
Avatar later, once the lookup had completed.</p>
 
1284
 
 
1285
<p>Here are some possible implementations of
 
1286
<code>MyRealm.requestAvatar</code>:</p>
 
1287
 
 
1288
<pre class="python"><p class="py-linenumber"> 1
 
1289
 2
 
1290
 3
 
1291
 4
 
1292
 5
 
1293
 6
 
1294
 7
 
1295
 8
 
1296
 9
 
1297
10
 
1298
11
 
1299
12
 
1300
13
 
1301
14
 
1302
15
 
1303
16
 
1304
17
 
1305
18
 
1306
19
 
1307
20
 
1308
21
 
1309
22
 
1310
23
 
1311
24
 
1312
25
 
1313
26
 
1314
27
 
1315
28
 
1316
29
 
1317
30
 
1318
31
 
1319
32
 
1320
33
 
1321
34
 
1322
35
 
1323
36
 
1324
37
 
1325
</p><span class="py-src-comment"># pre-existing, static avatars</span>
 
1326
    <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>):
 
1327
        <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>
 
1328
        <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>]
 
1329
        <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>
 
1330
 
 
1331
    <span class="py-src-comment"># database lookup and unpickling</span>
 
1332
    <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>):
 
1333
        <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>
 
1334
        <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>)
 
1335
        <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>)
 
1336
        <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>
 
1337
    <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>):
 
1338
        <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>)
 
1339
        <span class="py-src-keyword">return</span> <span class="py-src-variable">avatar</span>
 
1340
 
 
1341
    <span class="py-src-comment"># everybody shares the same Avatar</span>
 
1342
    <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>):
 
1343
        <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>
 
1344
        <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>
 
1345
 
 
1346
    <span class="py-src-comment"># anonymous users share one Avatar, named users each get their own</span>
 
1347
    <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>):
 
1348
        <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>
 
1349
        <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>:
 
1350
            <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>
 
1351
        <span class="py-src-keyword">else</span>:
 
1352
            <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>
 
1353
 
 
1354
    <span class="py-src-comment"># anonymous users get independent (but temporary) Avatars</span>
 
1355
    <span class="py-src-comment"># named users get their own persistent one</span>
 
1356
    <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>):
 
1357
        <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>
 
1358
        <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>:
 
1359
            <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>
 
1360
        <span class="py-src-keyword">else</span>:
 
1361
            <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>
 
1362
</pre>
 
1363
 
 
1364
<p>The last example, note that the new <code>MyAvatar</code> instance is not
 
1365
saved anywhere: it will vanish when the connection is dropped. By contrast,
 
1366
the avatars that live in the <code>self.avatars</code> dictionary will
 
1367
probably get persisted into the .tap file along with the Realm, the Portal,
 
1368
and anything else that is referenced by the top-level Application object.
 
1369
This is an easy way to manage saved user profiles.</p>
 
1370
 
 
1371
 
 
1372
<h3>Connecting and Disconnecting<a name="auto16"/></h3>
 
1373
 
 
1374
<p>It may be useful for your Avatars to be told when remote clients gain
 
1375
(and lose) access to them. For example, and Avatar might be updated by
 
1376
something in the server, and if there are clients attached, it should update
 
1377
them (through the <q>mind</q> argument which lets the Avatar do callRemote
 
1378
on the client).</p>
 
1379
 
 
1380
<p>One common idiom which accomplishes this is to have the Realm tell the
 
1381
avatar that a remote client has just attached. The Realm can also ask the
 
1382
protocol to let it know when the connection goes away, so it can then inform
 
1383
the Avatar that the client has detached. The third member of the
 
1384
<code>requestAvatar</code> return tuple is a callable which will be invoked
 
1385
when the connection is lost.</p>
 
1386
 
 
1387
<pre class="python"><p class="py-linenumber"> 1
 
1388
 2
 
1389
 3
 
1390
 4
 
1391
 5
 
1392
 6
 
1393
 7
 
1394
 8
 
1395
 9
 
1396
10
 
1397
11
 
1398
12
 
1399
13
 
1400
14
 
1401
15
 
1402
16
 
1403
17
 
1404
18
 
1405
19
 
1406
</p><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>):
 
1407
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
 
1408
        <span class="py-src-variable">self</span>.<span class="py-src-variable">clients</span> = []
 
1409
    <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>):
 
1410
        <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>)
 
1411
        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;attached to&quot;</span>, <span class="py-src-variable">mind</span>
 
1412
    <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>):
 
1413
        <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>)
 
1414
        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;detached from&quot;</span>, <span class="py-src-variable">mind</span>
 
1415
    <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>):
 
1416
        <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>:
 
1417
            <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>)
 
1418
 
 
1419
<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyRealm</span>:
 
1420
    <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>):
 
1421
        <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>
 
1422
        <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>]
 
1423
        <span class="py-src-variable">avatar</span>.<span class="py-src-variable">attached</span>(<span class="py-src-variable">mind</span>)
 
1424
        <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>)
 
1425
</pre>
 
1426
 
 
1427
 
 
1428
<h3>Viewable<a name="auto17"/></h3> <a name="viewable" shape="rect"/>
 
1429
 
 
1430
<p>Once you have <code>IPerspective</code> objects (i.e. the Avatar) to
 
1431
represent users, the <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.flavors.Viewable.html" title="twisted.spread.flavors.Viewable">Viewable</a></code> class can come into play. This
 
1432
class behaves a lot like <code>Referenceable</code>: it turns into a
 
1433
<code>RemoteReference</code> when sent over the wire, and certain methods
 
1434
can be invoked by the holder of that reference. However, the methods that
 
1435
can be called have names that start with <code>view_</code> instead of
 
1436
<code>remote_</code>, and those methods are always called with an extra
 
1437
<code>perspective</code> argument that points to the Avatar through which
 
1438
the reference was sent:</p>
 
1439
 
 
1440
<pre class="python"><p class="py-linenumber">1
 
1441
2
 
1442
3
 
1443
</p><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>):
 
1444
    <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>):
 
1445
        <span class="py-src-keyword">pass</span>
 
1446
</pre>
 
1447
 
 
1448
<p>This is useful if you want to let multiple clients share a reference to
 
1449
the same object. The <code>view_</code> methods can use the
 
1450
<q>perspective</q> argument to figure out which client is calling them. This
 
1451
gives them a way to do additional permission checks, do per-user accounting,
 
1452
etc.</p>
 
1453
 
 
1454
<p>This is the shortcut which makes per-user-per-group capability objects
 
1455
much easier to use. Instead of creating such per-(user,group) objects, you
 
1456
just have per-group objects which inherit from <code>pb.Viewable</code>, and
 
1457
give the user references to them. The local <code>pb.Avatar</code> object
 
1458
will automatically show up as the <q>perspective</q> argument in the
 
1459
<code>view_*</code> method calls, give you a chance to involve the Avatar in
 
1460
the process.</p>
 
1461
 
 
1462
 
 
1463
<h3>Chat Server with Avatars<a name="auto18"/></h3>
 
1464
 
 
1465
<p>Combining all the above techniques, here is an example chat server which
 
1466
uses a fixed set of identities (say, for the three members of your bridge
 
1467
club, who hang out in <q>#NeedAFourth</q> hoping that someone will discover
 
1468
your server, guess somebody's password, break in, join the group, and also
 
1469
be available for a game next saturday afternoon).</p>
 
1470
 
 
1471
<div class="py-listing"><pre><p class="py-linenumber"> 1
 
1472
 2
 
1473
 3
 
1474
 4
 
1475
 5
 
1476
 6
 
1477
 7
 
1478
 8
 
1479
 9
 
1480
10
 
1481
11
 
1482
12
 
1483
13
 
1484
14
 
1485
15
 
1486
16
 
1487
17
 
1488
18
 
1489
19
 
1490
20
 
1491
21
 
1492
22
 
1493
23
 
1494
24
 
1495
25
 
1496
26
 
1497
27
 
1498
28
 
1499
29
 
1500
30
 
1501
31
 
1502
32
 
1503
33
 
1504
34
 
1505
35
 
1506
36
 
1507
37
 
1508
38
 
1509
39
 
1510
40
 
1511
41
 
1512
42
 
1513
43
 
1514
44
 
1515
45
 
1516
46
 
1517
47
 
1518
48
 
1519
49
 
1520
50
 
1521
51
 
1522
52
 
1523
53
 
1524
54
 
1525
55
 
1526
56
 
1527
57
 
1528
58
 
1529
59
 
1530
60
 
1531
61
 
1532
62
 
1533
63
 
1534
64
 
1535
65
 
1536
</p><span class="py-src-comment">#!/usr/bin/env python</span>
 
1537
 
 
1538
<span class="py-src-comment"># Copyright (c) 2009 Twisted Matrix Laboratories.</span>
 
1539
<span class="py-src-comment"># See LICENSE for details.</span>
 
1540
 
 
1541
<span class="py-src-keyword">from</span> <span class="py-src-variable">zope</span>.<span class="py-src-variable">interface</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">implements</span>
 
1542
 
 
1543
<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>
 
1544
<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>
 
1545
<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>
 
1546
 
 
1547
<span class="py-src-keyword">class</span> <span class="py-src-identifier">ChatServer</span>:
 
1548
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>):
 
1549
        <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span> = {} <span class="py-src-comment"># indexed by name</span>
 
1550
 
 
1551
    <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>):
 
1552
        <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>):
 
1553
            <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>)
 
1554
        <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>)
 
1555
        <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>]
 
1556
 
 
1557
<span class="py-src-keyword">class</span> <span class="py-src-identifier">ChatRealm</span>:
 
1558
    <span class="py-src-variable">implements</span>(<span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>)
 
1559
    <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>):
 
1560
        <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>
 
1561
        <span class="py-src-variable">avatar</span> = <span class="py-src-variable">User</span>(<span class="py-src-variable">avatarID</span>)
 
1562
        <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>
 
1563
        <span class="py-src-variable">avatar</span>.<span class="py-src-variable">attached</span>(<span class="py-src-variable">mind</span>)
 
1564
        <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>)
 
1565
 
 
1566
<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>):
 
1567
    <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>):
 
1568
        <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span>
 
1569
    <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>):
 
1570
        <span class="py-src-variable">self</span>.<span class="py-src-variable">remote</span> = <span class="py-src-variable">mind</span>
 
1571
    <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>):
 
1572
        <span class="py-src-variable">self</span>.<span class="py-src-variable">remote</span> = <span class="py-src-variable">None</span>
 
1573
    <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>):
 
1574
        <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>)
 
1575
    <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>):
 
1576
        <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>)
 
1577
 
 
1578
<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>):
 
1579
    <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>):
 
1580
        <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">groupname</span>
 
1581
        <span class="py-src-variable">self</span>.<span class="py-src-variable">allowMattress</span> = <span class="py-src-variable">allowMattress</span>
 
1582
        <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span> = []
 
1583
    <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>):
 
1584
        <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>)
 
1585
    <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>):
 
1586
        <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>:
 
1587
            <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>
 
1588
        <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>:
 
1589
            <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>))
 
1590
 
 
1591
<span class="py-src-variable">realm</span> = <span class="py-src-variable">ChatRealm</span>()
 
1592
<span class="py-src-variable">realm</span>.<span class="py-src-variable">server</span> = <span class="py-src-variable">ChatServer</span>()
 
1593
<span class="py-src-variable">checker</span> = <span class="py-src-variable">checkers</span>.<span class="py-src-variable">InMemoryUsernamePasswordDatabaseDontUse</span>()
 
1594
<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>)
 
1595
<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>)
 
1596
<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>)
 
1597
<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>])
 
1598
 
 
1599
<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>))
 
1600
<span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
 
1601
</pre><div class="caption">Source listing - <a href="listings/pb/chatserver.py"><span class="filename">listings/pb/chatserver.py</span></a></div></div>
 
1602
 
 
1603
<p>Notice that the client uses <code>perspective_joinGroup</code> to both
 
1604
join a group and retrieve a <code>RemoteReference</code> to the
 
1605
<code>Group</code> object. However, the reference they get is actually to a
 
1606
special intermediate object called a <code>pb.ViewPoint</code>. When they do
 
1607
<code>group.callRemote(&quot;send&quot;, &quot;message&quot;)</code>, their avatar is inserted
 
1608
into the argument list that <code>Group.view_send</code> actually sees. This
 
1609
lets the group get their username out of the Avatar without giving the
 
1610
client an opportunity to spoof someone else.</p>
 
1611
 
 
1612
<p>The client side code that joins a group and sends a message would look
 
1613
like this:</p>
 
1614
 
 
1615
<div class="py-listing"><pre><p class="py-linenumber"> 1
 
1616
 2
 
1617
 3
 
1618
 4
 
1619
 5
 
1620
 6
 
1621
 7
 
1622
 8
 
1623
 9
 
1624
10
 
1625
11
 
1626
12
 
1627
13
 
1628
14
 
1629
15
 
1630
16
 
1631
17
 
1632
18
 
1633
19
 
1634
20
 
1635
21
 
1636
22
 
1637
23
 
1638
24
 
1639
25
 
1640
26
 
1641
27
 
1642
28
 
1643
29
 
1644
30
 
1645
31
 
1646
32
 
1647
33
 
1648
34
 
1649
35
 
1650
36
 
1651
37
 
1652
38
 
1653
39
 
1654
40
 
1655
</p><span class="py-src-comment">#!/usr/bin/env python</span>
 
1656
 
 
1657
<span class="py-src-comment"># Copyright (c) 2009 Twisted Matrix Laboratories.</span>
 
1658
<span class="py-src-comment"># See LICENSE for details.</span>
 
1659
 
 
1660
<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>
 
1661
<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>
 
1662
<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>
 
1663
 
 
1664
<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>):
 
1665
 
 
1666
    <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>):
 
1667
        <span class="py-src-keyword">print</span> <span class="py-src-variable">message</span>
 
1668
 
 
1669
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">connect</span>(<span class="py-src-parameter">self</span>):
 
1670
        <span class="py-src-variable">factory</span> = <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBClientFactory</span>()
 
1671
        <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>)
 
1672
        <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>),
 
1673
                             <span class="py-src-variable">client</span>=<span class="py-src-variable">self</span>)
 
1674
        <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>)
 
1675
        <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
 
1676
 
 
1677
    <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>):
 
1678
        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;connected, joining group #lookingForFourth&quot;</span>
 
1679
        <span class="py-src-comment"># this perspective is a reference to our User object</span>
 
1680
        <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>)
 
1681
        <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>)
 
1682
 
 
1683
    <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>):
 
1684
        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;joined group, now sending a message to all members&quot;</span>
 
1685
        <span class="py-src-comment"># 'group' is a reference to the Group object (through a ViewPoint)</span>
 
1686
        <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>)
 
1687
        <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>)
 
1688
 
 
1689
    <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>):
 
1690
        <span class="py-src-variable">reactor</span>.<span class="py-src-variable">stop</span>()
 
1691
 
 
1692
 
 
1693
<span class="py-src-variable">Client</span>().<span class="py-src-variable">connect</span>()
 
1694
</pre><div class="caption">Source listing - <a href="listings/pb/chatclient.py"><span class="filename">listings/pb/chatclient.py</span></a></div></div>
 
1695
 
 
1696
 
 
1697
<h2>Footnotes</h2><ol><li><a name="footnote-1"><span class="footnote">Apparently Alice is one of those weirdos who has nothing
 
1698
better to do than to try and impersonate Bob. She will lie to her chat
 
1699
client, send incorrect objects to remote methods, even rewrite her local
 
1700
client code entirely to accomplish this juvenile prank. Given this
 
1701
adversarial relationship, one must wonder why she and Bob seem to spend so
 
1702
much time together: their adventures are clearly documented by the
 
1703
cryptographic literature.</span></a></li><li><a name="footnote-2"><span class="footnote">the
 
1704
obvious name is clearly
 
1705
<code>ServerSidePerUserObjectWhichNobodyElseHasAccessTo</code>, but because
 
1706
python makes everything else so easy to read, it only seems fair to make
 
1707
your audience work for <em>something</em></span></a></li><li><a name="footnote-3"><span class="footnote">We could just go ahead and rename Perspective Broker to be
 
1708
Avatar Broker, but 1) that would cause massive compatibility problems, and 2)
 
1709
<q>AB</q> doesn't fit into the whole sandwich-themed naming scheme nearly as
 
1710
well as <q>PB</q> does. If we changed it to AB, we'd probably have to change
 
1711
Banana to be CD (CoderDecoder), and Jelly to be EF (EncapsulatorFragmentor).
 
1712
twisted.spread would then have to be renamed twisted.alphabetsoup, and then
 
1713
the whole food-pun thing would start all over again.</span></a></li><li><a name="footnote-4"><span class="footnote">The avatar-ish class is named
 
1714
<code>pb.Avatar</code> because <code>pb.Perspective</code> was already
 
1715
taken, by the (now obsolete) oldcred perspective-ish class. It is a pity,
 
1716
but it simply wasn't possible both replace <code>pb.Perspective</code>
 
1717
in-place <em>and</em> maintain a reasonable level of
 
1718
backwards-compatibility.</span></a></li></ol></div>
 
1719
 
 
1720
    <p><a href="index.html">Index</a></p>
 
1721
    <span class="version">Version: 10.0.0</span>
 
1722
  </body>
 
1723
</html>
 
 
b'\\ No newline at end of file'