summaryrefslogtreecommitdiff
path: root/doc/core/howto/pb-cred.html
diff options
context:
space:
mode:
Diffstat (limited to 'doc/core/howto/pb-cred.html')
-rw-r--r--doc/core/howto/pb-cred.html1724
1 files changed, 1724 insertions, 0 deletions
diff --git a/doc/core/howto/pb-cred.html b/doc/core/howto/pb-cred.html
new file mode 100644
index 0000000..1599806
--- /dev/null
+++ b/doc/core/howto/pb-cred.html
@@ -0,0 +1,1724 @@
+<?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">
+ <head>
+<title>Twisted Documentation: Authentication with Perspective Broker</title>
+<link href="stylesheet.css" rel="stylesheet" type="text/css"/>
+ </head>
+
+ <body bgcolor="white">
+ <h1 class="title">Authentication with Perspective Broker</h1>
+ <div class="toc"><ol><li><a href="#auto0">Overview</a></li><li><a href="#auto1">Compartmentalizing Services</a></li><ul><li><a href="#auto2">Incorrect Arguments</a></li><li><a href="#auto3">Unforgeable References</a></li><li><a href="#auto4">Argument Typechecking</a></li><li><a href="#auto5">Objects as Capabilities</a></li></ul><li><a href="#auto6">Avatars and Perspectives</a></li><li><a href="#auto7">Perspective Examples</a></li><ul><li><a href="#auto8">One Client</a></li><li><a href="#auto9">Two Clients</a></li><li><a href="#auto10">How that example worked</a></li><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>
+ <div class="content">
+<span/>
+
+<h2>Overview<a name="auto0"/></h2>
+
+<p>The examples shown in <a href="pb-usage.html" shape="rect">Using Perspective
+Broker</a> demonstrate how to do basic remote method calls, but provided no
+facilities for authentication. In this context, authentication is about who
+gets which remote references, and how to restrict access to the <q>right</q>
+set of people or programs.</p>
+
+<p>As soon as you have a program which offers services to multiple users,
+where those users should not be allowed to interfere with each other, you
+need to think about authentication. Many services use the idea of an
+<q>account</q>, and rely upon fact that each user has access to only one
+account. Twisted uses a system called <a href="cred.html" shape="rect">cred</a> to
+handle authentication issues, and Perspective Broker has code to make it
+easy to implement the most common use cases.</p>
+
+<h2>Compartmentalizing Services<a name="auto1"/></h2>
+
+<p>Imagine how you would write a chat server using PB. The first step might
+be a <code>ChatServer</code> object which had a bunch of
+ <code>pb.RemoteReference</code>s that point at user clients. Pretend that
+those clients offered a <code>remote_print</code> method which lets the
+server print a message on the user's console. In that case, the server might
+look something like this:</p>
+
+<pre class="python"><p class="py-linenumber"> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+</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>):
+
+ <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-variable">self</span>.<span class="py-src-variable">groups</span> = {} <span class="py-src-comment"># indexed by name</span>
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span> = {} <span class="py-src-comment"># indexed by name</span>
+ <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>):
+ <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>):
+ <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">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>])
+ <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>):
+ <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>]
+ <span class="py-src-keyword">if</span> <span class="py-src-variable">group</span>:
+ <span class="py-src-comment"># send the message to all members of the group</span>
+ <span class="py-src-keyword">for</span> <span class="py-src-variable">user</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">group</span>:
+ <span class="py-src-variable">user</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;print&quot;</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>))
+</pre>
+
+<p>For now, assume that all clients have somehow acquired a
+ <code>pb.RemoteReference</code> to this <code>ChatServer</code> object,
+perhaps using <code>pb.Root</code> and <code>getRootObject</code> as
+described in the <a href="pb-usage.html" shape="rect">previous chapter</a>. In this
+scheme, when a user sends a message to the group, their client runs
+something like the following:</p>
+
+<pre class="python"><p class="py-linenumber">1
+</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>)
+</pre>
+
+
+<h3>Incorrect Arguments<a name="auto2"/></h3>
+
+<p>You've probably seen the first problem: users can trivially spoof each
+other. We depend upon the user to pass a correct value in their
+ <q>username</q> argument, and have no way to tell if they're lying or not.
+There is nothing to prevent Alice from modifying her client to do:</p>
+
+<pre class="python"><p class="py-linenumber">1
+</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>)
+</pre>
+
+<p>much to the horror of Bob's vegetarian friends.<a href="#footnote-1" title="Apparently Alice is one of those weirdos who has nothing better to do than to try and impersonate Bob. She will lie to her chat client, send incorrect objects to remote methods, even rewrite her local client code entirely to accomplish this juvenile prank. Given this adversarial relationship, one must wonder why she and Bob seem to spend so much time together: their adventures are clearly documented by the cryptographic literature."><super>1</super></a></p>
+
+<p>(In general, learn to get suspicious if you see any argument of a
+remotely-invokable method described as <q>must be X</q>)</p>
+
+<p>The best way to fix this is to keep track of the user's name locally,
+rather than asking them to send it to the server with each message. The best
+place to keep state is in an object, so this suggests we need a per-user
+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
+ <code>User</code> class.
+</p>
+
+<pre class="python"><p class="py-linenumber"> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+</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>):
+ <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>):
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">username</span>
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">server</span> = <span class="py-src-variable">server</span>
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">remote</span> = <span class="py-src-variable">clientref</span>
+ <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-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-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>):
+ <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>)
+ <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>):
+ <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>)
+
+<span class="py-src-keyword">class</span> <span class="py-src-identifier">ChatServer</span>:
+ <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-variable">self</span>.<span class="py-src-variable">groups</span> = {} <span class="py-src-comment"># indexed by name</span>
+ <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-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>):
+ <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">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>)
+ <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>):
+ <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>]
+ <span class="py-src-keyword">if</span> <span class="py-src-variable">group</span>:
+ <span class="py-src-comment"># send the message to all members of the group</span>
+ <span class="py-src-keyword">for</span> <span class="py-src-variable">user</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">group</span>:
+ <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>))
+</pre>
+
+<p>Again, assume that each remote client gets access to a single
+ <code>User</code> object, which is created with the proper username.</p>
+
+<p>Note how the <code>ChatServer</code> object has no remote access: it
+isn't even <code>pb.Referenceable</code> anymore. This means that all access
+to it must be mediated through other objects, with code that is under your
+control.</p>
+
+<p>As long as Alice only has access to her own <code>User</code> object, she
+can no longer spoof Bob. The only way for her to invoke
+ <code>ChatServer.sendMessage</code> is to call her <code>User</code>
+object's <code>remote_sendMessage</code> method, and that method uses its
+own state to provide the <code>from_username</code> argument. It doesn't
+give her any way to change that state.</p>
+
+<p>This restriction is important. The <code>User</code> object is able to
+maintain its own integrity because there is a wall between the object and
+the client: the client cannot inspect or modify internal state, like the
+ <code>.name</code> attribute. The only way through this wall is via remote
+method invocations, and the only control Alice has over those invocations is
+when they get invoked and what arguments they are given.</p>
+
+<div class="note"><strong>Note: </strong>
+<p>No object can maintain its integrity against local threats: by design,
+Python offers no mechanism for class instances to hide their attributes, and
+once an intruder has a copy of <code>self.__dict__</code>, they can do
+everything the original object was able to do.</p>
+</div>
+
+
+<h3>Unforgeable References<a name="auto3"/></h3>
+
+<p>Now suppose you wanted to implement group parameters, for example a mode
+in which nobody was allowed to talk about mattresses because some users were
+sensitive and calming them down after someone said <q>mattress</q> is a
+hassle that's best avoided altogether. Again, per-group state implies a
+per-group object. We'll go out on a limb and call this the
+ <code>Group</code> object:</p>
+
+<pre class="python"><p class="py-linenumber"> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+</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>):
+ <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>):
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">username</span>
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">server</span> = <span class="py-src-variable">server</span>
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">remote</span> = <span class="py-src-variable">clientref</span>
+ <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>):
+ <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>)
+ <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>):
+ <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>)
+
+<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>):
+ <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>):
+ <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">self</span>.<span class="py-src-variable">allowMattress</span> = <span class="py-src-variable">allowMattress</span>
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span> = []
+ <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>):
+ <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-string">&quot;mattress&quot;</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">message</span>:
+ <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>
+ <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>:
+ <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>))
+ <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>):
+ <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>)
+
+<span class="py-src-keyword">class</span> <span class="py-src-identifier">ChatServer</span>:
+ <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-variable">self</span>.<span class="py-src-variable">groups</span> = {} <span class="py-src-comment"># indexed by name</span>
+ <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>):
+ <span class="py-src-keyword">if</span> <span class="py-src-variable">groupname</span> <span class="py-src-keyword">not</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">groups</span>:
+ <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>)
+ <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>)
+ <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>]
+</pre>
+
+
+<p>This example takes advantage of the fact that
+ <code>pb.Referenceable</code> objects sent over a wire can be returned to
+you, and they will be turned into references to the same object that you
+originally sent. The client cannot modify the object in any way: all they
+can do is point at it and invoke its <code>remote_*</code> methods. Thus,
+you can be sure that the <code>.name</code> attribute remains the same as
+you left it. In this case, the client code would look something like
+this:</p>
+
+<pre class="python"><p class="py-linenumber">1
+2
+3
+4
+5
+6
+7
+8
+9
+</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>):
+ <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>):
+ <span class="py-src-keyword">print</span> <span class="py-src-variable">message</span>
+ <span class="py-src-keyword">def</span> <span class="py-src-identifier">join</span>(<span class="py-src-parameter">self</span>):
+ <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>,
+ <span class="py-src-variable">allowMattress</span>=<span class="py-src-variable">False</span>)
+ <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>)
+ <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>):
+ <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>)
+</pre>
+
+<p>The <code>User</code> object is sent from the server side, and is turned
+into a <code>pb.RemoteReference</code> when it arrives at the client. The
+client sends it back to <code>Group.remote_send</code>, and PB turns it back
+into a reference to the original <code>User</code> when it gets there.
+ <code>Group.remote_send</code> can then use its <code>.name</code> attribute
+as the sender of the message.</p>
+
+<div class="note"><strong>Note: </strong>
+
+<p>Third party references (there aren't any)</p>
+
+<p>This technique also relies upon the fact that the
+ <code>pb.Referenceable</code> reference can <em>only</em> come from someone
+who holds a corresponding <code>pb.RemoteReference</code>. The design of the
+serialization mechanism (implemented in <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.spread.jelly.html" title="twisted.spread.jelly">twisted.spread.jelly</a></code>: pb, jelly, spread.. get it? Look for
+<q>banana</q>, too. What other networking framework
+can claim API names based on sandwich ingredients?) makes it impossible for
+a client to obtain a reference that they weren't explicitly given.
+References passed over the wire are given id numbers and recorded in a
+per-connection dictionary. If you didn't give them the reference, the id
+number won't be in the dict, and no amount of guessing by a malicious client
+will give them anything else. The dict goes away when the connection is
+dropped, further limiting the scope of those references.</p>
+
+<p>Futhermore, it is not possible for Bob to send <em>his</em>
+ <code>User</code> reference to Alice (perhaps over some other PB channel
+just between the two of them). Outside the context of Bob's connection to
+the server, that reference is just a meaningless number. To prevent
+confusion, PB will tell you if you try to give it away: when you try to hand
+a <code>pb.RemoteReference</code> to a third party, you'll get an exception
+(implemented with an assert in pb.py:364 RemoteReference.jellyFor).</p>
+
+<p>This helps the security model somewhat: only the client you gave the
+reference to can cause any damage with it. Of course, the client might be a
+brainless zombie, simply doing anything some third party wants. When it's
+not proxying <code>callRemote</code> invocations, it's probably terrorizing
+the living and searching out human brains for sustenance. In short, if you
+don't trust them, don't give them that reference.</p>
+
+<p>And remember that everything you've ever given them over that connection
+can come back to you. If expect the client to invoke your method with some
+object A that you sent to them earlier, and instead they send you object B
+(that you also sent to them earlier), and you don't check it somehow, then
+you've just opened up a security hole (we'll see an example of this
+shortly). It may be better to keep such objects in a dictionary on the
+server side, and have the client send you an index string instead. Doing it
+that way makes it obvious that they can send you anything they want, and
+improves the chances that you'll remember to implement the right checks.
+(This is exactly what PB is doing underneath, with a per-connection
+dictionary of <code>Referenceable</code> objects, indexed by a number).</p>
+
+<p>And, of course, you have to make sure you don't accidentally hand out a
+reference to the wrong object.</p>
+
+</div>
+
+
+<p>But again, note the vulnerability. If Alice holds a
+ <code>RemoteReference</code> to <em>any</em> object on the server side that
+has a <code>.name</code> attribute, she can use that name as a spoofed
+<q>from</q> parameter. As a simple example, what if her client code looked
+like:</p>
+
+<pre class="python"><p class="py-linenumber">1
+2
+3
+4
+5
+6
+</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>):
+ <span class="py-src-keyword">def</span> <span class="py-src-identifier">join</span>(<span class="py-src-parameter">self</span>):
+ <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>)
+ <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>)
+ <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>):
+ <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>)
+</pre>
+
+<p>This would let her send a message that appeared to come from
+<q>#twisted</q> rather than <q>Alice</q>. If she joined a group that
+happened to be named <q>bob</q> (perhaps it is the <q>How To Be Bob</q>
+channel, populated by Alice and countless others, a place where they can
+share stories about their best impersonating-Bob moments), then she would be
+able to emit a message that looked like <q>&lt;bob&gt; says: hi there</q>,
+and she has accomplished her lifelong goal.</p>
+
+
+<h3>Argument Typechecking<a name="auto4"/></h3>
+
+<p>There are two techniques to close this hole. The first is to have your
+remotely-invokable methods do type-checking on their arguments: if
+ <code>Group.remote_send</code> asserted <code>isinstance(from_user,
+User)</code> then Alice couldn't use non-User objects to do her spoofing,
+and hopefully the rest of the system is designed well enough to prevent her
+from obtaining access to somebody else's User object.</p>
+
+
+<h3>Objects as Capabilities<a name="auto5"/></h3>
+
+<p>The second technique is to avoid having the client send you the objects
+altogether. If they don't send you anything, there is nothing to verify. In
+this case, you would have to have a per-user-per-group object, in which the
+ <code>remote_send</code> method would only take a single
+ <code>message</code> argument. The <code>UserGroup</code> object is created
+with references to the only <code>User</code> and <code>Group</code> objects
+that it will ever use, so no lookups are needed:</p>
+
+<pre class="python"><p class="py-linenumber"> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+</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>):
+ <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>):
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">user</span> = <span class="py-src-variable">user</span>
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">group</span> = <span class="py-src-variable">group</span>
+ <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>):
+ <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>)
+
+<span class="py-src-keyword">class</span> <span class="py-src-identifier">Group</span>:
+ <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>):
+ <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">self</span>.<span class="py-src-variable">allowMattress</span> = <span class="py-src-variable">allowMattress</span>
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span> = []
+ <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>):
+ <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-string">&quot;mattress&quot;</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">message</span>:
+ <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>
+ <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>:
+ <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>))
+ <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>):
+ <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>)
+</pre>
+
+<p>The only message-sending method Alice has left is
+ <code>UserGroup.remote_send</code>, and it only accepts a message: there are
+no remaining ways to influence the <q>from</q> name.</p>
+
+<p>In this model, each remotely-accessible object represents a very small
+set of capabilities. Security is achieved by only granting a minimal set of
+abilities to each remote user.</p>
+
+<p>PB provides a shortcut which makes this technique easier to use. The
+ <code>Viewable</code> class will be discussed <a href="#viewable" shape="rect">below</a>.</p>
+
+<h2>Avatars and Perspectives<a name="auto6"/></h2>
+
+<p>In Twisted's <a href="cred.html" shape="rect">cred</a> system, an <q>Avatar</q> is
+an object that lives on the <q>server</q> side (defined here as the side
+farthest from the human who is trying to get something done) which lets the
+remote user get something done. The avatar isn't really a particular class,
+it's more like a description of a role that some object plays, as in <q>the
+Foo object here is acting as the user's avatar for this particular
+service</q>. Generally, the remote user has some way of getting their avatar
+to run some code. The avatar object may enforce some security checks, and
+provide additional data, then call other methods which get things done.</p>
+
+<p>The two pieces in the cred puzzle (for any protocol, not just PB) are:
+<q>what serves as the Avatar?</q>, and <q>how does the user get access to
+it?</q>.</p>
+
+<p>For PB, the first question is easy. The Avatar is a remotely-accessible
+object which can run code: this is a perfect description of
+ <code>pb.Referenceable</code> and its subclasses. We shall defer the second
+question until the next section.</p>
+
+<p>In the example above, you can think of the <code>ChatServer</code> and
+ <code>Group</code> objects as a service. The <code>User</code> object is the
+user's server-side representative: everything the user is capable of doing
+is done by running one of its methods. Anything that the server wants to do
+to the user (change their group membership, change their name, delete their
+pet cat, whatever) is done by manipulating the <code>User</code> object.</p>
+
+<p>There are multiple User objects living in peace and harmony around the
+ChatServer. Each has a different point of view on the services provided by
+the ChatServer and the Groups: each may belong to different groups, some
+might have more permissions than others (like the ability to create groups).
+These different points of view are called <q>Perspectives</q>. This is the
+origin of the term <q>Perspective</q> in <q>Perspective Broker</q>: PB
+provides and controls (i.e. <q>brokers</q>) access to Perspectives.</p>
+
+<p>Once upon a time, these local-representative objects were actually called
+ <code>pb.Perspective</code>. But this has changed with the advent of the
+rewritten cred system, and now the more generic term for a local
+representative object is an Avatar. But you will still see reference to
+ <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
+that perspectives and avatars are basically the same thing. </p>
+
+<p>Despite all we've been <a href="cred.html" shape="rect">telling you</a> about how
+Avatars are more of a concept than an actual class, the base class from
+which you can create your server-side avatar-ish objects is, in fact, named
+ <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
+ <code>pb.Referenceable</code>. The only difference is that instead of
+offering <q>remote_FOO</q> methods, they offer <q>perspective_FOO</q>
+methods.</p>
+
+<p>The other way in which <code>pb.Avatar</code> differs from
+ <code>pb.Referenceable</code> is that the avatar objects are designed to be
+the first thing retrieved by a cred-using remote client. Just as
+ <code>PBClientFactory.getRootObject</code> gives the client access to a
+ <code>pb.Root</code> object (which can then provide access to all kinds of
+other objects), <code>PBClientFactory.login</code> gives client access to a
+ <code>pb.Avatar</code> object (which can return other references). </p>
+
+<p>So, the first half of using cred in your PB application is to create an
+Avatar object which implements <code>perspective_</code> methods and is
+careful to do useful things for the remote user while remaining vigilant
+against being tricked with unexpected argument values. It must also be
+careful to never give access to objects that the user should not have access
+to, whether by returning them directly, returning objects which contain
+them, or returning objects which can be asked (remotely) to provide
+them.</p>
+
+<p>The second half is how the user gets a <code>pb.RemoteReference</code> to
+your Avatar. As explained <a href="cred.html" shape="rect">elsewhere</a>, Avatars are
+obtained from a Realm. The Realm doesn't deal with authentication at all
+(usernames, passwords, public keys, challenge-response systems, retinal
+scanners, real-time DNA sequencers, etc). It simply takes an <q>avatarID</q>
+(which is effectively a username) and returns an Avatar object. The Portal
+and its Checkers deal with authenticating the user: by the time they are
+done, the remote user has proved their right to access the avatarID that is
+given to the Realm, so the Realm can return a remotely-controllable object
+that has whatever powers you wish to grant to this particular user. </p>
+
+<p>For PB, the realm is expected to return a <code>pb.Avatar</code> (or
+anything which implements <code>pb.IPerspective</code>, really, but there's
+no reason to not return a <code>pb.Avatar</code> subclass). This object will
+be given to the client just like a <code>pb.Root</code> would be without
+cred, and the user can get access to other objects through it (if you let
+them).</p>
+
+<p>The basic idea is that there is a separate IPerspective-implementing
+object (i.e. the Avatar subclass) (i.e. the <q>perspective</q>) for each
+user, and <em>only</em> the authorized user gets a remote reference to that
+object. You can store whatever permissions or capabilities the user
+possesses in that object, and then use them when the user invokes a remote
+method. You give the user access to the perspective object instead of the
+objects that do the real work.</p>
+
+
+<h2>Perspective Examples<a name="auto7"/></h2>
+
+<p>Here is a brief example of using a pb.Avatar. Most of the support code
+is magic for now: we'll explain it later.</p>
+
+<h3>One Client<a name="auto8"/></h3>
+
+<div class="py-listing"><pre><p class="py-linenumber"> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+</p><span class="py-src-comment">#!/usr/bin/env python</span>
+
+<span class="py-src-comment"># Copyright (c) Twisted Matrix Laboratories.</span>
+<span class="py-src-comment"># See LICENSE for details.</span>
+
+<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>
+
+<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>
+<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>
+<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>
+
+<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>):
+ <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>):
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span>
+ <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>):
+ <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>
+
+<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyRealm</span>:
+ <span class="py-src-variable">implements</span>(<span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>)
+ <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarId</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
+ <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>:
+ <span class="py-src-keyword">raise</span> <span class="py-src-variable">NotImplementedError</span>
+ <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>
+
+<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>())
+<span class="py-src-variable">p</span>.<span class="py-src-variable">registerChecker</span>(
+ <span class="py-src-variable">checkers</span>.<span class="py-src-variable">InMemoryUsernamePasswordDatabaseDontUse</span>(<span class="py-src-variable">user1</span>=<span class="py-src-string">&quot;pass1&quot;</span>))
+<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>))
+<span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
+</pre><div class="caption">Source listing - <a href="listings/pb/pb5server.py"><span class="filename">listings/pb/pb5server.py</span></a></div></div>
+<div class="py-listing"><pre><p class="py-linenumber"> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+</p><span class="py-src-comment">#!/usr/bin/env python</span>
+
+<span class="py-src-comment"># Copyright (c) Twisted Matrix Laboratories.</span>
+<span class="py-src-comment"># See LICENSE for details.</span>
+
+<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>
+<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>
+<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>
+
+<span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
+ <span class="py-src-variable">factory</span> = <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBClientFactory</span>()
+ <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>)
+ <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>))
+ <span class="py-src-variable">def1</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">connected</span>)
+ <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
+
+<span class="py-src-keyword">def</span> <span class="py-src-identifier">connected</span>(<span class="py-src-parameter">perspective</span>):
+ <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>
+ <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;asking it to foo(12)&quot;</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">12</span>)
+
+<span class="py-src-variable">main</span>()
+</pre><div class="caption">Source listing - <a href="listings/pb/pb5client.py"><span class="filename">listings/pb/pb5client.py</span></a></div></div>
+
+<p>Ok, so that wasn't really very exciting. It doesn't accomplish much more
+than the first PB example, and used a lot more code to do it. Let's try it
+again with two users this time.</p>
+
+<div class="note"><strong>Note: </strong>
+
+<p>When the client runs <code>login</code> to request the Perspective,
+they can provide it with an optional <code>client</code> argument (which
+must be a <code>pb.Referenceable</code> object). If they do, then a
+reference to that object will be handed to the realm's
+ <code>requestAvatar</code> in the <code>mind</code> argument.</p>
+
+<p>The server-side Perspective can use it to invoke remote methods on
+something in the client, so that the client doesn't always have to drive the
+interaction. In a chat server, the client object would be the one to which
+<q>display text</q> messages were sent. In a board game server, this would
+provide a way to tell the clients that someone has made a move, so they can
+update their game boards.</p>
+
+</div>
+
+<h3>Two Clients<a name="auto9"/></h3>
+
+<div class="py-listing"><pre><p class="py-linenumber"> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+</p><span class="py-src-comment">#!/usr/bin/env python</span>
+
+<span class="py-src-comment"># Copyright (c) Twisted Matrix Laboratories.</span>
+<span class="py-src-comment"># See LICENSE for details.</span>
+
+<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>
+
+<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>
+<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>
+<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>
+
+<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>):
+ <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>):
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span>
+ <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>):
+ <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>
+
+<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyRealm</span>:
+ <span class="py-src-variable">implements</span>(<span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>)
+ <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarId</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
+ <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>:
+ <span class="py-src-keyword">raise</span> <span class="py-src-variable">NotImplementedError</span>
+ <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>
+
+<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>())
+<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>,
+ <span class="py-src-variable">user2</span>=<span class="py-src-string">&quot;pass2&quot;</span>)
+<span class="py-src-variable">p</span>.<span class="py-src-variable">registerChecker</span>(<span class="py-src-variable">c</span>)
+<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>))
+<span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
+</pre><div class="caption">Source listing - <a href="listings/pb/pb6server.py"><span class="filename">listings/pb/pb6server.py</span></a></div></div>
+<div class="py-listing"><pre><p class="py-linenumber"> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+</p><span class="py-src-comment">#!/usr/bin/env python</span>
+
+<span class="py-src-comment"># Copyright (c) Twisted Matrix Laboratories.</span>
+<span class="py-src-comment"># See LICENSE for details.</span>
+
+<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>
+<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>
+<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>
+
+<span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
+ <span class="py-src-variable">factory</span> = <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBClientFactory</span>()
+ <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>)
+ <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>))
+ <span class="py-src-variable">def1</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">connected</span>)
+ <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
+
+<span class="py-src-keyword">def</span> <span class="py-src-identifier">connected</span>(<span class="py-src-parameter">perspective</span>):
+ <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>
+ <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;asking it to foo(13)&quot;</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>)
+
+<span class="py-src-variable">main</span>()
+</pre><div class="caption">Source listing - <a href="listings/pb/pb6client1.py"><span class="filename">listings/pb/pb6client1.py</span></a></div></div>
+<div class="py-listing"><pre><p class="py-linenumber"> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+</p><span class="py-src-comment">#!/usr/bin/env python</span>
+
+<span class="py-src-comment"># Copyright (c) Twisted Matrix Laboratories.</span>
+<span class="py-src-comment"># See LICENSE for details.</span>
+
+<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>
+<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>
+
+<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>
+<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>
+<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>
+
+<span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
+ <span class="py-src-variable">factory</span> = <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBClientFactory</span>()
+ <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>)
+ <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>))
+ <span class="py-src-variable">def1</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">connected</span>)
+ <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
+
+<span class="py-src-keyword">def</span> <span class="py-src-identifier">connected</span>(<span class="py-src-parameter">perspective</span>):
+ <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>
+ <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;asking it to foo(14)&quot;</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">14</span>)
+
+<span class="py-src-variable">main</span>()
+</pre><div class="caption">Source listing - <a href="listings/pb/pb6client2.py"><span class="filename">listings/pb/pb6client2.py</span></a></div></div>
+
+<p>While pb6server.py is running, try starting pb6client1, then pb6client2.
+Compare the argument passed by the <code>.callRemote()</code> in each
+client. You can see how each client gets connected to a different
+Perspective.</p>
+
+
+<h3>How that example worked<a name="auto10"/></h3><a name="smallexample" shape="rect"/>
+
+<p>Let's walk through the previous example and see what was going on.</p>
+
+<p>First, we created a subclass called <code>MyPerspective</code> which is
+our server-side Avatar. It implements a <code>perspective_foo</code> method
+that is exposed to the remote client.</p>
+
+<p>Second, we created a realm (an object which implements
+ <code>IRealm</code>, and therefore implements <code>requestAvatar</code>).
+This realm manufactures <code>MyPerspective</code> objects. It makes as many
+as we want, and names each one with the avatarID (a username) that comes out
+of the checkers. This MyRealm object returns two other objects as well,
+which we will describe later.</p>
+
+<p>Third, we created a portal to hold this realm. The portal's job is to
+dispatch incoming clients to the credential checkers, and then to request
+Avatars for any which survive the authentication process.</p>
+
+<p>Fourth, we made a simple checker (an object which implements
+ <code>IChecker</code>) to hold valid user/password pairs. The checker
+gets registered with the portal, so it knows who to ask when new
+clients connect. We use a checker named
+ <code>InMemoryUsernamePasswordDatabaseDontUse</code>, which suggests
+that 1: all the username/password pairs are kept in memory instead of
+being saved to a database or something, and 2: you shouldn't use
+it. The admonition against using it is because there are better
+schemes: keeping everything in memory will not work when you have
+thousands or millions of users to keep track of, the passwords will be
+stored in the .tap file when the application shuts down (possibly a
+security risk), and finally it is a nuisance to add or remove users
+after the checker is constructed.</p>
+
+<p>Fifth, we create a <code>pb.PBServerFactory</code> to listen on a TCP
+port. This factory knows how to connect the remote client to the Portal, so
+incoming connections will be handed to the authentication process. Other
+protocols (non-PB) would do something similar: the factory that creates
+Protocol objects will give those objects access to the Portal so
+authentication can take place.</p>
+
+<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
+connection completes, the factory will be asked to produce a Protocol, and
+it will create a PB object. Unlike the previous chapter, where we used
+ <code>.getRootObject</code>, here we use <code>factory.login</code> to
+ initiate the cred authentication process. We provide a
+ <code>credentials</code> object, which is the client-side agent for doing
+our half of the authentication process. This process may involve several
+messages: challenges, responses, encrypted passwords, secure hashes, etc. We
+give our credentials object everything it will need to respond correctly (in
+this case, a username and password, but you could write a credential that
+used public-key encryption or even fancier techniques).</p>
+
+<p><code>login</code> returns a Deferred which, when it fires, will return a
+ <code>pb.RemoteReference</code> to the remote avatar. We can then do
+ <code>callRemote</code> to invoke a <code>perspective_foo</code> method on
+that Avatar.</p>
+
+
+<h3>Anonymous Clients<a name="auto11"/></h3>
+
+<div class="py-listing"><pre><p class="py-linenumber"> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+</p><span class="py-src-comment">#!/usr/bin/env python</span>
+
+<span class="py-src-comment"># Copyright (c) Twisted Matrix Laboratories.</span>
+<span class="py-src-comment"># See LICENSE for details.</span>
+
+<span class="py-src-string">&quot;&quot;&quot;
+Implement the realm for and run on port 8800 a PB service which allows both
+anonymous and username/password based access.
+
+Successful username/password-based login requests given an instance of
+MyPerspective with a name which matches the username with which they
+authenticated. Success anonymous login requests are given an instance of
+MyPerspective with the name &quot;Anonymous&quot;.
+&quot;&quot;&quot;</span>
+
+<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>
+
+<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>
+
+<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>
+<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>
+<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>
+<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>
+<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>
+<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>
+
+
+<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyPerspective</span>(<span class="py-src-parameter">Avatar</span>):
+ <span class="py-src-string">&quot;&quot;&quot;
+ Trivial avatar exposing a single remote method for demonstrative
+ purposes. All successful login attempts in this example will result in
+ an avatar which is an instance of this class.
+
+ @type name: C{str}
+ @ivar name: The username which was used during login or C{&quot;Anonymous&quot;}
+ if the login was anonymous (a real service might want to avoid the
+ collision this introduces between anonoymous users and authenticated
+ users named &quot;Anonymous&quot;).
+ &quot;&quot;&quot;</span>
+ <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>):
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span>
+
+
+ <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>):
+ <span class="py-src-string">&quot;&quot;&quot;
+ Print a simple message which gives the argument this method was
+ called with and this avatar's name.
+ &quot;&quot;&quot;</span>
+ <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;I am %s. perspective_foo(%s) called on %s.&quot;</span> % (
+ <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>)
+
+
+
+<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyRealm</span>(<span class="py-src-parameter">object</span>):
+ <span class="py-src-string">&quot;&quot;&quot;
+ Trivial realm which supports anonymous and named users by creating
+ avatars which are instances of MyPerspective for either.
+ &quot;&quot;&quot;</span>
+ <span class="py-src-variable">implements</span>(<span class="py-src-variable">IRealm</span>)
+
+ <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarId</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
+ <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>:
+ <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>)
+ <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>:
+ <span class="py-src-variable">avatarId</span> = <span class="py-src-string">&quot;Anonymous&quot;</span>
+ <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>
+
+
+
+<span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
+ <span class="py-src-string">&quot;&quot;&quot;
+ Create a PB server using MyRealm and run it on port 8800.
+ &quot;&quot;&quot;</span>
+ <span class="py-src-variable">startLogging</span>(<span class="py-src-variable">stdout</span>)
+
+ <span class="py-src-variable">p</span> = <span class="py-src-variable">Portal</span>(<span class="py-src-variable">MyRealm</span>())
+
+ <span class="py-src-comment"># Here the username/password checker is registered.</span>
+ <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>)
+ <span class="py-src-variable">p</span>.<span class="py-src-variable">registerChecker</span>(<span class="py-src-variable">c1</span>)
+
+ <span class="py-src-comment"># Here the anonymous checker is registered.</span>
+ <span class="py-src-variable">c2</span> = <span class="py-src-variable">AllowAnonymousAccess</span>()
+ <span class="py-src-variable">p</span>.<span class="py-src-variable">registerChecker</span>(<span class="py-src-variable">c2</span>)
+
+ <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>))
+ <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
+
+
+<span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> == <span class="py-src-string">'__main__'</span>:
+ <span class="py-src-variable">main</span>()
+</pre><div class="caption">Source listing - <a href="listings/pb/pbAnonServer.py"><span class="filename">listings/pb/pbAnonServer.py</span></a></div></div>
+<div class="py-listing"><pre><p class="py-linenumber"> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+</p><span class="py-src-comment">#!/usr/bin/env python</span>
+
+<span class="py-src-comment"># Copyright (c) Twisted Matrix Laboratories.</span>
+<span class="py-src-comment"># See LICENSE for details.</span>
+
+<span class="py-src-string">&quot;&quot;&quot;
+Client which will talk to the server run by pbAnonServer.py, logging in
+either anonymously or with username/password credentials.
+&quot;&quot;&quot;</span>
+
+<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>
+
+<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>
+<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>
+<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>
+<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>
+<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>
+
+
+<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>):
+ <span class="py-src-string">&quot;&quot;&quot;
+ Catch-all errback which simply logs the failure. This isn't expected to
+ be invoked in the normal case for this example.
+ &quot;&quot;&quot;</span>
+ <span class="py-src-variable">err</span>(<span class="py-src-variable">why</span>, <span class="py-src-variable">msg</span>)
+
+
+<span class="py-src-keyword">def</span> <span class="py-src-identifier">connected</span>(<span class="py-src-parameter">perspective</span>):
+ <span class="py-src-string">&quot;&quot;&quot;
+ Login callback which invokes the remote &quot;foo&quot; method on the perspective
+ which the server returned.
+ &quot;&quot;&quot;</span>
+ <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>
+ <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;asking it to foo(13)&quot;</span>
+ <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>)
+
+
+<span class="py-src-keyword">def</span> <span class="py-src-identifier">finished</span>(<span class="py-src-parameter">ignored</span>):
+ <span class="py-src-string">&quot;&quot;&quot;
+ Callback invoked when both logins and method calls have finished to shut
+ down the reactor so the example exits.
+ &quot;&quot;&quot;</span>
+ <span class="py-src-variable">reactor</span>.<span class="py-src-variable">stop</span>()
+
+
+<span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span>():
+ <span class="py-src-string">&quot;&quot;&quot;
+ Connect to a PB server running on port 8800 on localhost and log in to
+ it, both anonymously and using a username/password it will recognize.
+ &quot;&quot;&quot;</span>
+ <span class="py-src-variable">startLogging</span>(<span class="py-src-variable">stdout</span>)
+ <span class="py-src-variable">factory</span> = <span class="py-src-variable">PBClientFactory</span>()
+ <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>)
+
+ <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>())
+ <span class="py-src-variable">anonymousLogin</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">connected</span>)
+ <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>)
+
+ <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>))
+ <span class="py-src-variable">usernameLogin</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">connected</span>)
+ <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>)
+
+ <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>])
+ <span class="py-src-variable">bothDeferreds</span>.<span class="py-src-variable">addCallback</span>(<span class="py-src-variable">finished</span>)
+
+ <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
+
+
+<span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> == <span class="py-src-string">'__main__'</span>:
+ <span class="py-src-variable">main</span>()
+</pre><div class="caption">Source listing - <a href="listings/pb/pbAnonClient.py"><span class="filename">listings/pb/pbAnonClient.py</span></a></div></div>
+
+<p>pbAnonServer.py implements a server based on pb6server.py, extending it to
+permit anonymous logins in addition to authenticated logins. An
+ <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.cred.checkers.AllowAnonymousAccess.html" title="twisted.cred.checkers.AllowAnonymousAccess">AllowAnonymousAccess</a></code>
+checker and an <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.cred.checkers.InMemoryUsernamePasswordDatabaseDontUse.html" title="twisted.cred.checkers.InMemoryUsernamePasswordDatabaseDontUse">InMemoryUsernamePasswordDatabaseDontUse</a></code>
+checker are registered and the
+client's choice of credentials object determines which is used to authenticate
+the login. In either case, the realm will be called on to create an avatar for
+the login. <code>AllowAnonymousAccess</code> always produces an <code>avatarId
+ </code> of <code>twisted.cred.checkers.ANONYMOUS</code>.</p>
+
+<p>On the client side, the only change is the use of an instance of
+ <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.cred.credentials.Anonymous.html" title="twisted.cred.credentials.Anonymous">Anonymous</a></code> when calling
+ <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.spread.pb.PBClientFactory.login.html" title="twisted.spread.pb.PBClientFactory.login">PBClientFactory.login</a></code>.</p>
+
+
+<h2>Using Avatars<a name="auto12"/></h2>
+
+
+<h3>Avatar Interfaces<a name="auto13"/></h3>
+
+<p>The first element of the 3-tuple returned by <code>requestAvatar</code>
+indicates which Interface this Avatar implements. For PB avatars, it will
+always be <code>pb.IPerspective</code>, because that's the only interface
+these avatars implement.</p>
+
+<p>This element is present because <code>requestAvatar</code> is actually
+presented with a list of possible Interfaces. The question being posed to
+the Realm is: <q>do you have an avatar for (avatarID) that can implement one
+of the following set of Interfaces?</q>. Some portals and checkers might
+give a list of Interfaces and the Realm could pick; the PB code only knows
+how to do one, so we cannot take advantage of this feature.</p>
+
+<h3>Logging Out<a name="auto14"/></h3>
+
+<p>The third element of the 3-tuple is a zero-argument callable, which will
+be invoked by the protocol when the connection has been lost. We can use
+this to notify the Avatar when the client has lost its connection. This will
+be described in more detail below.</p>
+
+<h3>Making Avatars<a name="auto15"/></h3>
+
+<p>In the example above, we create Avatars upon request, during
+ <code>requestAvatar</code>. Depending upon the service, these Avatars might
+already exist before the connection is received, and might outlive the
+connection. The Avatars might also accept multiple connections.</p>
+
+<p>Another possibility is that the Avatars might exist ahead of time, but in
+a different form (frozen in a pickle and/or saved in a database). In this
+case, <code>requestAvatar</code> may need to perform a database lookup and
+then do something with the result before it can provide an avatar. In this
+case, it would probably return a Deferred so it could provide the real
+Avatar later, once the lookup had completed.</p>
+
+<p>Here are some possible implementations of
+ <code>MyRealm.requestAvatar</code>:</p>
+
+<pre class="python"><p class="py-linenumber"> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+</p><span class="py-src-comment"># pre-existing, static avatars</span>
+ <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarID</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
+ <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>
+ <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>]
+ <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>
+
+ <span class="py-src-comment"># database lookup and unpickling</span>
+ <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarID</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
+ <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>
+ <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>)
+ <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>)
+ <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>
+ <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>):
+ <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>)
+ <span class="py-src-keyword">return</span> <span class="py-src-variable">avatar</span>
+
+ <span class="py-src-comment"># everybody shares the same Avatar</span>
+ <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarID</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
+ <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>
+ <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>
+
+ <span class="py-src-comment"># anonymous users share one Avatar, named users each get their own</span>
+ <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarID</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
+ <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>
+ <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>:
+ <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>
+ <span class="py-src-keyword">else</span>:
+ <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>
+
+ <span class="py-src-comment"># anonymous users get independent (but temporary) Avatars</span>
+ <span class="py-src-comment"># named users get their own persistent one</span>
+ <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarID</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
+ <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>
+ <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>:
+ <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>
+ <span class="py-src-keyword">else</span>:
+ <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>
+</pre>
+
+<p>The last example, note that the new <code>MyAvatar</code> instance is not
+saved anywhere: it will vanish when the connection is dropped. By contrast,
+the avatars that live in the <code>self.avatars</code> dictionary will
+probably get persisted into the .tap file along with the Realm, the Portal,
+and anything else that is referenced by the top-level Application object.
+This is an easy way to manage saved user profiles.</p>
+
+
+<h3>Connecting and Disconnecting<a name="auto16"/></h3>
+
+<p>It may be useful for your Avatars to be told when remote clients gain
+(and lose) access to them. For example, and Avatar might be updated by
+something in the server, and if there are clients attached, it should update
+them (through the <q>mind</q> argument which lets the Avatar do callRemote
+on the client).</p>
+
+<p>One common idiom which accomplishes this is to have the Realm tell the
+avatar that a remote client has just attached. The Realm can also ask the
+protocol to let it know when the connection goes away, so it can then inform
+the Avatar that the client has detached. The third member of the
+ <code>requestAvatar</code> return tuple is a callable which will be invoked
+when the connection is lost.</p>
+
+<pre class="python"><p class="py-linenumber"> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+</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>):
+ <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-variable">self</span>.<span class="py-src-variable">clients</span> = []
+ <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>):
+ <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>)
+ <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;attached to&quot;</span>, <span class="py-src-variable">mind</span>
+ <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>):
+ <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>)
+ <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;detached from&quot;</span>, <span class="py-src-variable">mind</span>
+ <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>):
+ <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>:
+ <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>)
+
+<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyRealm</span>:
+ <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarID</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
+ <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>
+ <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>]
+ <span class="py-src-variable">avatar</span>.<span class="py-src-variable">attached</span>(<span class="py-src-variable">mind</span>)
+ <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>)
+</pre>
+
+
+<h3>Viewable<a name="auto17"/></h3> <a name="viewable" shape="rect"/>
+
+<p>Once you have <code>IPerspective</code> objects (i.e. the Avatar) to
+represent users, the <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.spread.pb.Viewable.html" title="twisted.spread.pb.Viewable">Viewable</a></code> class can come into play. This
+class behaves a lot like <code>Referenceable</code>: it turns into a
+ <code>RemoteReference</code> when sent over the wire, and certain methods
+can be invoked by the holder of that reference. However, the methods that
+can be called have names that start with <code>view_</code> instead of <code>remote_</code>, and those methods are always called with an extra
+<code>perspective</code> argument that points to the Avatar through which
+the reference was sent:</p>
+
+<pre class="python"><p class="py-linenumber">1
+2
+3
+</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>):
+ <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>):
+ <span class="py-src-keyword">pass</span>
+</pre>
+
+<p>This is useful if you want to let multiple clients share a reference to
+the same object. The <code>view_</code> methods can use the
+ <q>perspective</q> argument to figure out which client is calling them. This
+gives them a way to do additional permission checks, do per-user accounting,
+etc.</p>
+
+<p>This is the shortcut which makes per-user-per-group capability objects
+much easier to use. Instead of creating such per-(user,group) objects, you
+just have per-group objects which inherit from <code>pb.Viewable</code>, and
+give the user references to them. The local <code>pb.Avatar</code> object
+will automatically show up as the <q>perspective</q> argument in the
+ <code>view_*</code> method calls, give you a chance to involve the Avatar in
+the process.</p>
+
+
+<h3>Chat Server with Avatars<a name="auto18"/></h3>
+
+<p>Combining all the above techniques, here is an example chat server which
+uses a fixed set of identities (say, for the three members of your bridge
+club, who hang out in <q>#NeedAFourth</q> hoping that someone will discover
+your server, guess somebody's password, break in, join the group, and also
+be available for a game next saturday afternoon).</p>
+
+<div class="py-listing"><pre><p class="py-linenumber"> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+</p><span class="py-src-comment">#!/usr/bin/env python</span>
+
+<span class="py-src-comment"># Copyright (c) Twisted Matrix Laboratories.</span>
+<span class="py-src-comment"># See LICENSE for details.</span>
+
+<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>
+
+<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>
+<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>
+<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>
+
+<span class="py-src-keyword">class</span> <span class="py-src-identifier">ChatServer</span>:
+ <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-variable">self</span>.<span class="py-src-variable">groups</span> = {} <span class="py-src-comment"># indexed by name</span>
+
+ <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>):
+ <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>):
+ <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>)
+ <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>)
+ <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>]
+
+<span class="py-src-keyword">class</span> <span class="py-src-identifier">ChatRealm</span>:
+ <span class="py-src-variable">implements</span>(<span class="py-src-variable">portal</span>.<span class="py-src-variable">IRealm</span>)
+ <span class="py-src-keyword">def</span> <span class="py-src-identifier">requestAvatar</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">avatarID</span>, <span class="py-src-parameter">mind</span>, *<span class="py-src-parameter">interfaces</span>):
+ <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>
+ <span class="py-src-variable">avatar</span> = <span class="py-src-variable">User</span>(<span class="py-src-variable">avatarID</span>)
+ <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>
+ <span class="py-src-variable">avatar</span>.<span class="py-src-variable">attached</span>(<span class="py-src-variable">mind</span>)
+ <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>)
+
+<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>):
+ <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>):
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">name</span> = <span class="py-src-variable">name</span>
+ <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>):
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">remote</span> = <span class="py-src-variable">mind</span>
+ <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>):
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">remote</span> = <span class="py-src-variable">None</span>
+ <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>):
+ <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>)
+ <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>):
+ <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>)
+
+<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>):
+ <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>):
+ <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">self</span>.<span class="py-src-variable">allowMattress</span> = <span class="py-src-variable">allowMattress</span>
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">users</span> = []
+ <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>):
+ <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>)
+ <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>):
+ <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-string">&quot;mattress&quot;</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">message</span>:
+ <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>
+ <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>:
+ <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>))
+
+<span class="py-src-variable">realm</span> = <span class="py-src-variable">ChatRealm</span>()
+<span class="py-src-variable">realm</span>.<span class="py-src-variable">server</span> = <span class="py-src-variable">ChatServer</span>()
+<span class="py-src-variable">checker</span> = <span class="py-src-variable">checkers</span>.<span class="py-src-variable">InMemoryUsernamePasswordDatabaseDontUse</span>()
+<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>)
+<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>)
+<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>)
+<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>])
+
+<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>))
+<span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
+</pre><div class="caption">Source listing - <a href="listings/pb/chatserver.py"><span class="filename">listings/pb/chatserver.py</span></a></div></div>
+
+<p>Notice that the client uses <code>perspective_joinGroup</code> to both
+join a group and retrieve a <code>RemoteReference</code> to the
+ <code>Group</code> object. However, the reference they get is actually to a
+special intermediate object called a <code>pb.ViewPoint</code>. When they do
+ <code>group.callRemote(&quot;send&quot;, &quot;message&quot;)</code>, their avatar is inserted
+into the argument list that <code>Group.view_send</code> actually sees. This
+lets the group get their username out of the Avatar without giving the
+client an opportunity to spoof someone else.</p>
+
+<p>The client side code that joins a group and sends a message would look
+like this:</p>
+
+<div class="py-listing"><pre><p class="py-linenumber"> 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+</p><span class="py-src-comment">#!/usr/bin/env python</span>
+<span class="py-src-comment"># Copyright (c) Twisted Matrix Laboratories.</span>
+<span class="py-src-comment"># See LICENSE for details.</span>
+
+<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>
+<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>
+<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>
+
+<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>):
+
+ <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>):
+ <span class="py-src-keyword">print</span> <span class="py-src-variable">message</span>
+
+ <span class="py-src-keyword">def</span> <span class="py-src-identifier">connect</span>(<span class="py-src-parameter">self</span>):
+ <span class="py-src-variable">factory</span> = <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBClientFactory</span>()
+ <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>)
+ <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>),
+ <span class="py-src-variable">client</span>=<span class="py-src-variable">self</span>)
+ <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>)
+ <span class="py-src-variable">reactor</span>.<span class="py-src-variable">run</span>()
+
+ <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>):
+ <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;connected, joining group #NeedAFourth&quot;</span>
+ <span class="py-src-comment"># this perspective is a reference to our User object. Save a reference</span>
+ <span class="py-src-comment"># to it here, otherwise it will get garbage collected after this call,</span>
+ <span class="py-src-comment"># and the server will think we logged out.</span>
+ <span class="py-src-variable">self</span>.<span class="py-src-variable">perspective</span> = <span class="py-src-variable">perspective</span>
+ <span class="py-src-variable">d</span> = <span class="py-src-variable">perspective</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;joinGroup&quot;</span>, <span class="py-src-string">&quot;#NeedAFourth&quot;</span>)
+ <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>)
+
+ <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>):
+ <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;joined group, now sending a message to all members&quot;</span>
+ <span class="py-src-comment"># 'group' is a reference to the Group object (through a ViewPoint)</span>
+ <span class="py-src-variable">d</span> = <span class="py-src-variable">group</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;send&quot;</span>, <span class="py-src-string">&quot;You can call me Al.&quot;</span>)
+ <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>)
+
+ <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>):
+ <span class="py-src-variable">reactor</span>.<span class="py-src-variable">stop</span>()
+
+
+<span class="py-src-variable">Client</span>().<span class="py-src-variable">connect</span>()
+</pre><div class="caption">Source listing - <a href="listings/pb/chatclient.py"><span class="filename">listings/pb/chatclient.py</span></a></div></div>
+
+
+<h2>Footnotes</h2><ol><li><a name="footnote-1"><span class="footnote">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.</span></a></li><li><a name="footnote-2"><span class="footnote">The
+obvious name is clearly
+<code>ServerSidePerUserObjectWhichNobodyElseHasAccessTo</code>, but because
+Python makes everything else so easy to read, it only seems fair to make
+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
+Avatar Broker, but 1) that would cause massive compatibility problems, and 2)
+<q>AB</q> doesn't fit into the whole sandwich-themed naming scheme nearly as
+well as <q>PB</q> 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.</span></a></li><li><a name="footnote-4"><span class="footnote">The avatar-ish class is named
+<code>pb.Avatar</code> because <code>pb.Perspective</code> was already
+taken, by the (now obsolete) oldcred perspective-ish class. It is a pity,
+but it simply wasn't possible both replace <code>pb.Perspective</code>
+in-place <em>and</em> maintain a reasonable level of
+backwards-compatibility.</span></a></li></ol></div>
+
+ <p><a href="index.html">Index</a></p>
+ <span class="version">Version: 12.1.0</span>
+ </body>
+</html> \ No newline at end of file