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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
<?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 were 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-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-variable">message</span>.<span class="py-src-variable">find</span>(<span class="py-src-string">&quot;mattress&quot;</span>) != -<span class="py-src-number">1</span>:
            <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-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>]
</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/10.0.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-variable">message</span>.<span class="py-src-variable">find</span>(<span class="py-src-string">&quot;mattress&quot;</span>) != -<span class="py-src-number">1</span>:
            <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) 2009 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) 2009 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) 2009 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) 2009 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) 2009 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) 2007-2009 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) 2007-2009 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. A
<code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.cred.checkers.AllowAnonymousAccess.html" title="twisted.cred.checkers.AllowAnonymousAccess">AllowAnonymousAccess</a></code>
checker and a <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.cred.checkers.
.html" title="twisted.cred.checkers.
">
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 class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.cred.checkers.ANONYMOUS.html" title="twisted.cred.checkers.ANONYMOUS">ANONYMOUS</a></code>.</p>

<p>On the client side, the only change is the use of an instance of
<code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.cred.credentials.Anonymous.html" title="twisted.cred.credentials.Anonymous">Anonymous</a></code> when calling
<code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.spread.pb.PBClientFactory.login.html" title="twisted.spread.pb.PBClientFactory.login">PBClientFactory.login</a></code>.</p>


<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/10.0.0/api/twisted.spread.flavors.Viewable.html" title="twisted.spread.flavors.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) 2009 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-variable">message</span>.<span class="py-src-variable">find</span>(<span class="py-src-string">&quot;mattress&quot;</span>) != -<span class="py-src-number">1</span>:
            <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
</p><span class="py-src-comment">#!/usr/bin/env python</span>

<span class="py-src-comment"># Copyright (c) 2009 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 #lookingForFourth&quot;</span>
        <span class="py-src-comment"># this perspective is a reference to our User object</span>
        <span class="py-src-variable">d</span> = <span class="py-src-variable">perspective</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;joinGroup&quot;</span>, <span class="py-src-string">&quot;#lookingForFourth&quot;</span>)
        <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: 10.0.0</span>
  </body>
</html>