This file is indexed.

/usr/share/pyshared/zope.app.authentication-3.9.egg-info/PKG-INFO is in python-zope.app.authentication 3.9-0ubuntu1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

   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
Metadata-Version: 1.1
Name: zope.app.authentication
Version: 3.9
Summary: Principals and groups management for the pluggable authentication utility
Home-page: http://pypi.python.org/pypi/zope.app.authentication
Author: Zope Corporation and Contributors
Author-email: zope-dev@zope.org
License: ZPL 2.1
Description: This package provides a flexible and pluggable authentication utility for Zope
        3, using `zope.pluggableauth`. Several common plugins are provided.
        
        
        .. contents::
        
        ================================
        Pluggable-Authentication Utility
        ================================
        
        The Pluggable-Authentication Utility (PAU) provides a framework for
        authenticating principals and associating information with them. It uses
        plugins and subscribers to get its work done.
        
        For a pluggable-authentication utility to be used, it should be
        registered as a utility providing the
        `zope.authentication.interfaces.IAuthentication` interface.
        
        Authentication
        --------------
        
        The primary job of PAU is to authenticate principals. It uses two types of
        plug-ins in its work:
        
          - Credentials Plugins
        
          - Authenticator Plugins
        
        Credentials plugins are responsible for extracting user credentials from a
        request. A credentials plugin may in some cases issue a 'challenge' to obtain
        credentials. For example, a 'session' credentials plugin reads credentials
        from a session (the "extraction"). If it cannot find credentials, it will
        redirect the user to a login form in order to provide them (the "challenge").
        
        Authenticator plugins are responsible for authenticating the credentials
        extracted by a credentials plugin. They are also typically able to create
        principal objects for credentials they successfully authenticate.
        
        Given a request object, the PAU returns a principal object, if it can. The PAU
        does this by first iterateing through its credentials plugins to obtain a
        set of credentials. If it gets credentials, it iterates through its
        authenticator plugins to authenticate them.
        
        If an authenticator succeeds in authenticating a set of credentials, the PAU
        uses the authenticator to create a principal corresponding to the credentials.
        The authenticator notifies subscribers if an authenticated principal is
        created. Subscribers are responsible for adding data, especially groups, to
        the principal. Typically, if a subscriber adds data, it should also add
        corresponding interface declarations.
        
        Simple Credentials Plugin
        ~~~~~~~~~~~~~~~~~~~~~~~~~
        
        To illustrate, we'll create a simple credentials plugin::
        
          >>> from zope import interface
          >>> from zope.app.authentication import interfaces
        
          >>> class MyCredentialsPlugin(object):
          ...
          ...     interface.implements(interfaces.ICredentialsPlugin)
          ...
          ...     def extractCredentials(self, request):
          ...         return request.get('credentials')
          ...
          ...     def challenge(self, request):
          ...         pass # challenge is a no-op for this plugin
          ...
          ...     def logout(self, request):
          ...         pass # logout is a no-op for this plugin
        
        As a plugin, MyCredentialsPlugin needs to be registered as a named utility::
        
          >>> myCredentialsPlugin = MyCredentialsPlugin()
          >>> provideUtility(myCredentialsPlugin, name='My Credentials Plugin')
        
        Simple Authenticator Plugin
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~
        
        Next we'll create a simple authenticator plugin. For our plugin, we'll need
        an implementation of IPrincipalInfo::
        
          >>> class PrincipalInfo(object):
          ...
          ...     interface.implements(interfaces.IPrincipalInfo)
          ...
          ...     def __init__(self, id, title, description):
          ...         self.id = id
          ...         self.title = title
          ...         self.description = description
          ...
          ...     def __repr__(self):
          ...         return 'PrincipalInfo(%r)' % self.id
        
        Our authenticator uses this type when it creates a principal info::
        
          >>> class MyAuthenticatorPlugin(object):
          ...
          ...     interface.implements(interfaces.IAuthenticatorPlugin)
          ...
          ...     def authenticateCredentials(self, credentials):
          ...         if credentials == 'secretcode':
          ...             return PrincipalInfo('bob', 'Bob', '')
          ...
          ...     def principalInfo(self, id):
          ...         pass # plugin not currently supporting search
        
        As with the credentials plugin, the authenticator plugin must be registered
        as a named utility::
        
          >>> myAuthenticatorPlugin = MyAuthenticatorPlugin()
          >>> provideUtility(myAuthenticatorPlugin, name='My Authenticator Plugin')
        
        Principal Factories
        ~~~~~~~~~~~~~~~~~~~
        
        While authenticator plugins provide principal info, they are not responsible
        for creating principals. This function is performed by factory adapters. For
        these tests we'll borrow some factories from the principal folder::
        
          >>> from zope.app.authentication import principalfolder
          >>> provideAdapter(principalfolder.AuthenticatedPrincipalFactory)
          >>> provideAdapter(principalfolder.FoundPrincipalFactory)
        
        For more information on these factories, see their docstrings.
        
        Configuring a PAU
        ~~~~~~~~~~~~~~~~~
        
        Finally, we'll create the PAU itself::
        
          >>> from zope.app import authentication
          >>> pau = authentication.PluggableAuthentication('xyz_')
        
        and configure it with the two plugins::
        
          >>> pau.credentialsPlugins = ('My Credentials Plugin', )
          >>> pau.authenticatorPlugins = ('My Authenticator Plugin', )
        
        Using the PAU to Authenticate
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        
        We can now use the PAU to authenticate a sample request::
        
          >>> from zope.publisher.browser import TestRequest
          >>> print pau.authenticate(TestRequest())
          None
        
        In this case, we cannot authenticate an empty request. In the same way, we
        will not be able to authenticate a request with the wrong credentials::
        
          >>> print pau.authenticate(TestRequest(credentials='let me in!'))
          None
        
        However, if we provide the proper credentials::
        
          >>> request = TestRequest(credentials='secretcode')
          >>> principal = pau.authenticate(request)
          >>> principal
          Principal('xyz_bob')
        
        we get an authenticated principal.
        
        Authenticated Principal Creates Events
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        
        We can verify that the appropriate event was published::
        
          >>> [event] = getEvents(interfaces.IAuthenticatedPrincipalCreated)
          >>> event.principal is principal
          True
          >>> event.info
          PrincipalInfo('bob')
          >>> event.request is request
          True
        
        The info object has the id, title, and description of the principal.  The info
        object is also generated by the authenticator plugin, so the plugin may
        itself have provided additional information on the info object::
        
          >>> event.info.title
          'Bob'
          >>> event.info.id # does not include pau prefix
          'bob'
          >>> event.info.description
          ''
        
        It is also decorated with two other attributes, credentialsPlugin and
        authenticatorPlugin: these are the plugins used to extract credentials for and
        authenticate this principal.  These attributes can be useful for subscribers
        that want to react to the plugins used.  For instance, subscribers can
        determine that a given credential plugin does or does not support logout, and
        provide information usable to show or hide logout user interface::
        
          >>> event.info.credentialsPlugin is myCredentialsPlugin
          True
          >>> event.info.authenticatorPlugin is myAuthenticatorPlugin
          True
        
        Normally, we provide subscribers to these events that add additional
        information to the principal. For example, we'll add one that sets
        the title::
        
          >>> def add_info(event):
          ...     event.principal.title = event.info.title
          >>> provideHandler(add_info, [interfaces.IAuthenticatedPrincipalCreated])
        
        Now, if we authenticate a principal, its title is set::
        
          >>> principal = pau.authenticate(request)
          >>> principal.title
          'Bob'
        
        Multiple Authenticator Plugins
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        
        The PAU works with multiple authenticator plugins. It uses each plugin, in the
        order specified in the PAU's authenticatorPlugins attribute, to authenticate
        a set of credentials.
        
        To illustrate, we'll create another authenticator::
        
          >>> class MyAuthenticatorPlugin2(MyAuthenticatorPlugin):
          ...
          ...     def authenticateCredentials(self, credentials):
          ...         if credentials == 'secretcode':
          ...             return PrincipalInfo('black', 'Black Spy', '')
          ...         elif credentials == 'hiddenkey':
          ...             return PrincipalInfo('white', 'White Spy', '')
        
          >>> provideUtility(MyAuthenticatorPlugin2(), name='My Authenticator Plugin 2')
        
        If we put it before the original authenticator::
        
          >>> pau.authenticatorPlugins = (
          ...     'My Authenticator Plugin 2',
          ...     'My Authenticator Plugin')
        
        Then it will be given the first opportunity to authenticate a request::
        
          >>> pau.authenticate(TestRequest(credentials='secretcode'))
          Principal('xyz_black')
        
        If neither plugins can authenticate, pau returns None::
        
          >>> print pau.authenticate(TestRequest(credentials='let me in!!'))
          None
        
        When we change the order of the authenticator plugins::
        
          >>> pau.authenticatorPlugins = (
          ...     'My Authenticator Plugin',
          ...     'My Authenticator Plugin 2')
        
        we see that our original plugin is now acting first::
        
          >>> pau.authenticate(TestRequest(credentials='secretcode'))
          Principal('xyz_bob')
        
        The second plugin, however, gets a chance to authenticate if first does not::
        
          >>> pau.authenticate(TestRequest(credentials='hiddenkey'))
          Principal('xyz_white')
        
        Multiple Credentials Plugins
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        
        As with with authenticators, we can specify multiple credentials plugins. To
        illustrate, we'll create a credentials plugin that extracts credentials from
        a request form::
        
          >>> class FormCredentialsPlugin:
          ...
          ...     interface.implements(interfaces.ICredentialsPlugin)
          ...
          ...     def extractCredentials(self, request):
          ...         return request.form.get('my_credentials')
          ...
          ...     def challenge(self, request):
          ...         pass
          ...
          ...     def logout(request):
          ...         pass
        
          >>> provideUtility(FormCredentialsPlugin(),
          ...                name='Form Credentials Plugin')
        
        and insert the new credentials plugin before the existing plugin::
        
          >>> pau.credentialsPlugins = (
          ...     'Form Credentials Plugin',
          ...     'My Credentials Plugin')
        
        The PAU will use each plugin in order to try and obtain credentials from a
        request::
        
          >>> pau.authenticate(TestRequest(credentials='secretcode',
          ...                              form={'my_credentials': 'hiddenkey'}))
          Principal('xyz_white')
        
        In this case, the first credentials plugin succeeded in getting credentials
        from the form and the second authenticator was able to authenticate the
        credentials. Specifically, the PAU went through these steps:
        
         - Get credentials using 'Form Credentials Plugin'
        
         - Got 'hiddenkey' credentials using 'Form Credentials Plugin', try to
           authenticate using 'My Authenticator Plugin'
        
         - Failed to authenticate 'hiddenkey' with 'My Authenticator Plugin', try
           'My Authenticator Plugin 2'
        
         - Succeeded in authenticating with 'My Authenticator Plugin 2'
        
        Let's try a different scenario::
        
          >>> pau.authenticate(TestRequest(credentials='secretcode'))
          Principal('xyz_bob')
        
        In this case, the PAU went through these steps::
        
          - Get credentials using 'Form Credentials Plugin'
        
          - Failed to get credentials using 'Form Credentials Plugin', try
            'My Credentials Plugin'
        
          - Got 'scecretcode' credentials using 'My Credentials Plugin', try to
            authenticate using 'My Authenticator Plugin'
        
          - Succeeded in authenticating with 'My Authenticator Plugin'
        
        Let's try a slightly more complex scenario::
        
          >>> pau.authenticate(TestRequest(credentials='hiddenkey',
          ...                              form={'my_credentials': 'bogusvalue'}))
          Principal('xyz_white')
        
        This highlights PAU's ability to use multiple plugins for authentication:
        
          - Get credentials using 'Form Credentials Plugin'
        
          - Got 'bogusvalue' credentials using 'Form Credentials Plugin', try to
            authenticate using 'My Authenticator Plugin'
        
          - Failed to authenticate 'boguskey' with 'My Authenticator Plugin', try
            'My Authenticator Plugin 2'
        
          - Failed to authenticate 'boguskey' with 'My Authenticator Plugin 2' --
            there are no more authenticators to try, so lets try the next credentials
            plugin for some new credentials
        
          - Get credentials using 'My Credentials Plugin'
        
          - Got 'hiddenkey' credentials using 'My Credentials Plugin', try to
            authenticate using 'My Authenticator Plugin'
        
          - Failed to authenticate 'hiddenkey' using 'My Authenticator Plugin', try
            'My Authenticator Plugin 2'
        
          - Succeeded in authenticating with 'My Authenticator Plugin 2' (shouts and
            cheers!)
        
        
        Principal Searching
        -------------------
        
        As a component that provides IAuthentication, a PAU lets you lookup a
        principal with a principal ID. The PAU looks up a principal by delegating to
        its authenticators. In our example, none of the authenticators implement this
        search capability, so when we look for a principal::
        
          >>> print pau.getPrincipal('xyz_bob')
          Traceback (most recent call last):
          PrincipalLookupError: bob
        
          >>> print pau.getPrincipal('white')
          Traceback (most recent call last):
          PrincipalLookupError: white
        
          >>> print pau.getPrincipal('black')
          Traceback (most recent call last):
          PrincipalLookupError: black
        
        For a PAU to support search, it needs to be configured with one or more
        authenticator plugins that support search. To illustrate, we'll create a new
        authenticator::
        
          >>> class SearchableAuthenticatorPlugin:
          ...
          ...     interface.implements(interfaces.IAuthenticatorPlugin)
          ...
          ...     def __init__(self):
          ...         self.infos = {}
          ...         self.ids = {}
          ...
          ...     def principalInfo(self, id):
          ...         return self.infos.get(id)
          ...
          ...     def authenticateCredentials(self, credentials):
          ...         id = self.ids.get(credentials)
          ...         if id is not None:
          ...             return self.infos[id]
          ...
          ...     def add(self, id, title, description, credentials):
          ...         self.infos[id] = PrincipalInfo(id, title, description)
          ...         self.ids[credentials] = id
        
        This class is typical of an authenticator plugin. It can both authenticate
        principals and find principals given a ID. While there are cases
        where an authenticator may opt to not perform one of these two functions, they
        are less typical.
        
        As with any plugin, we need to register it as a utility::
        
          >>> searchable = SearchableAuthenticatorPlugin()
          >>> provideUtility(searchable, name='Searchable Authentication Plugin')
        
        We'll now configure the PAU to use only the searchable authenticator::
        
          >>> pau.authenticatorPlugins = ('Searchable Authentication Plugin',)
        
        and add some principals to the authenticator::
        
          >>> searchable.add('bob', 'Bob', 'A nice guy', 'b0b')
          >>> searchable.add('white', 'White Spy', 'Sneaky', 'deathtoblack')
        
        Now when we ask the PAU to find a principal::
        
          >>> pau.getPrincipal('xyz_bob')
          Principal('xyz_bob')
        
        but only those it knows about::
        
          >>> print pau.getPrincipal('black')
          Traceback (most recent call last):
          PrincipalLookupError: black
        
        Found Principal Creates Events
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        
        As evident in the authenticator's 'createFoundPrincipal' method (see above),
        a FoundPrincipalCreatedEvent is published when the authenticator finds a
        principal on behalf of PAU's 'getPrincipal'::
        
          >>> clearEvents()
          >>> principal = pau.getPrincipal('xyz_white')
          >>> principal
          Principal('xyz_white')
        
          >>> [event] = getEvents(interfaces.IFoundPrincipalCreated)
          >>> event.principal is principal
          True
          >>> event.info
          PrincipalInfo('white')
        
        The info has an authenticatorPlugin, but no credentialsPlugin, since none was
        used::
        
          >>> event.info.credentialsPlugin is None
          True
          >>> event.info.authenticatorPlugin is searchable
          True
        
        As we have seen with authenticated principals, it is common to subscribe to
        principal created events to add information to the newly created principal.
        In this case, we need to subscribe to IFoundPrincipalCreated events::
        
          >>> provideHandler(add_info, [interfaces.IFoundPrincipalCreated])
        
        Now when a principal is created as a result of a search, it's title and
        description will be set (by the add_info handler function).
        
        Multiple Authenticator Plugins
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        
        As with the other operations we've seen, the PAU uses multiple plugins to
        find a principal. If the first authenticator plugin can't find the requested
        principal, the next plugin is used, and so on.
        
        To illustrate, we'll create and register a second searchable authenticator::
        
          >>> searchable2 = SearchableAuthenticatorPlugin()
          >>> provideUtility(searchable2, name='Searchable Authentication Plugin 2')
        
        and add a principal to it::
        
          >>> searchable.add('black', 'Black Spy', 'Also sneaky', 'deathtowhite')
        
        When we configure the PAU to use both searchable authenticators (note the
        order)::
        
          >>> pau.authenticatorPlugins = (
          ...     'Searchable Authentication Plugin 2',
          ...     'Searchable Authentication Plugin')
        
        we see how the PAU uses both plugins::
        
          >>> pau.getPrincipal('xyz_white')
          Principal('xyz_white')
        
          >>> pau.getPrincipal('xyz_black')
          Principal('xyz_black')
        
        If more than one plugin know about the same principal ID, the first plugin is
        used and the remaining are not delegated to. To illustrate, we'll add
        another principal with the same ID as an existing principal::
        
          >>> searchable2.add('white', 'White Rider', '', 'r1der')
          >>> pau.getPrincipal('xyz_white').title
          'White Rider'
        
        If we change the order of the plugins::
        
          >>> pau.authenticatorPlugins = (
          ...     'Searchable Authentication Plugin',
          ...     'Searchable Authentication Plugin 2')
        
        we get a different principal for ID 'white'::
        
          >>> pau.getPrincipal('xyz_white').title
          'White Spy'
        
        
        Issuing a Challenge
        -------------------
        
        Part of PAU's IAuthentication contract is to challenge the user for
        credentials when its 'unauthorized' method is called. The need for this
        functionality is driven by the following use case:
        
          - A user attempts to perform an operation he is not authorized to perform.
        
          - A handler responds to the unauthorized error by calling IAuthentication
            'unauthorized'.
        
          - The authentication component (in our case, a PAU) issues a challenge to
            the user to collect new credentials (typically in the form of logging in
            as a new user).
        
        The PAU handles the credentials challenge by delegating to its credentials
        plugins.
        
        Currently, the PAU is configured with the credentials plugins that don't
        perform any action when asked to challenge (see above the 'challenge' methods).
        
        To illustrate challenges, we'll subclass an existing credentials plugin and
        do something in its 'challenge'::
        
          >>> class LoginFormCredentialsPlugin(FormCredentialsPlugin):
          ...
          ...     def __init__(self, loginForm):
          ...         self.loginForm = loginForm
          ...
          ...     def challenge(self, request):
          ...         request.response.redirect(self.loginForm)
          ...         return True
        
        This plugin handles a challenge by redirecting the response to a login form.
        It returns True to signal to the PAU that it handled the challenge.
        
        We will now create and register a couple of these plugins::
        
          >>> provideUtility(LoginFormCredentialsPlugin('simplelogin.html'),
          ...                name='Simple Login Form Plugin')
        
          >>> provideUtility(LoginFormCredentialsPlugin('advancedlogin.html'),
          ...                name='Advanced Login Form Plugin')
        
        and configure the PAU to use them::
        
          >>> pau.credentialsPlugins = (
          ...     'Simple Login Form Plugin',
          ...     'Advanced Login Form Plugin')
        
        Now when we call 'unauthorized' on the PAU::
        
          >>> request = TestRequest()
          >>> pau.unauthorized(id=None, request=request)
        
        we see that the user is redirected to the simple login form::
        
          >>> request.response.getStatus()
          302
          >>> request.response.getHeader('location')
          'simplelogin.html'
        
        We can change the challenge policy by reordering the plugins::
        
          >>> pau.credentialsPlugins = (
          ...     'Advanced Login Form Plugin',
          ...     'Simple Login Form Plugin')
        
        Now when we call 'unauthorized'::
        
          >>> request = TestRequest()
          >>> pau.unauthorized(id=None, request=request)
        
        the advanced plugin is used because it's first::
        
          >>> request.response.getStatus()
          302
          >>> request.response.getHeader('location')
          'advancedlogin.html'
        
        Challenge Protocols
        ~~~~~~~~~~~~~~~~~~~
        
        Sometimes, we want multiple challengers to work together. For example, the
        HTTP specification allows multiple challenges to be issued in a response. A
        challenge plugin can provide a `challengeProtocol` attribute that effectively
        groups related plugins together for challenging. If a plugin returns `True`
        from its challenge and provides a non-None challengeProtocol, subsequent
        plugins in the credentialsPlugins list that have the same challenge protocol
        will also be used to challenge.
        
        Without a challengeProtocol, only the first plugin to succeed in a challenge
        will be used.
        
        Let's look at an example. We'll define a new plugin that specifies an
        'X-Challenge' protocol::
        
          >>> class XChallengeCredentialsPlugin(FormCredentialsPlugin):
          ...
          ...     challengeProtocol = 'X-Challenge'
          ...
          ...     def __init__(self, challengeValue):
          ...         self.challengeValue = challengeValue
          ...
          ...     def challenge(self, request):
          ...         value = self.challengeValue
          ...         existing = request.response.getHeader('X-Challenge', '')
          ...         if existing:
          ...             value += ' ' + existing
          ...         request.response.setHeader('X-Challenge', value)
          ...         return True
        
        and register a couple instances as utilities::
        
          >>> provideUtility(XChallengeCredentialsPlugin('basic'),
          ...                name='Basic X-Challenge Plugin')
        
          >>> provideUtility(XChallengeCredentialsPlugin('advanced'),
          ...                name='Advanced X-Challenge Plugin')
        
        When we use both plugins with the PAU::
        
          >>> pau.credentialsPlugins = (
          ...     'Basic X-Challenge Plugin',
          ...     'Advanced X-Challenge Plugin')
        
        and call 'unauthorized'::
        
          >>> request = TestRequest()
          >>> pau.unauthorized(None, request)
        
        we see that both plugins participate in the challange, rather than just the
        first plugin::
        
          >>> request.response.getHeader('X-Challenge')
          'advanced basic'
        
        
        Pluggable-Authentication Prefixes
        ---------------------------------
        
        Principal ids are required to be unique system wide. Plugins will often provide
        options for providing id prefixes, so that different sets of plugins provide
        unique ids within a PAU. If there are multiple pluggable-authentication
        utilities in a system, it's a good idea to give each PAU a unique prefix, so
        that principal ids from different PAUs don't conflict. We can provide a prefix
        when a PAU is created::
        
          >>> pau = authentication.PluggableAuthentication('mypau_')
          >>> pau.credentialsPlugins = ('My Credentials Plugin', )
          >>> pau.authenticatorPlugins = ('My Authenticator Plugin', )
        
        When we create a request and try to authenticate::
        
          >>> pau.authenticate(TestRequest(credentials='secretcode'))
          Principal('mypau_bob')
        
        Note that now, our principal's id has the pluggable-authentication
        utility prefix.
        
        We can still lookup a principal, as long as we supply the prefix::
        
          >> pau.getPrincipal('mypas_42')
          Principal('mypas_42', "{'domain': 42}")
        
          >> pau.getPrincipal('mypas_41')
          OddPrincipal('mypas_41', "{'int': 41}")
        
        
        Searching
        ---------
        
        PAU implements ISourceQueriables::
        
          >>> from zope.schema.interfaces import ISourceQueriables
          >>> ISourceQueriables.providedBy(pau)
          True
        
        This means a PAU can be used in a principal source vocabulary (Zope provides a
        sophisticated searching UI for principal sources).
        
        As we've seen, a PAU uses each of its authenticator plugins to locate a
        principal with a given ID. However, plugins may also provide the interface
        IQuerySchemaSearch to indicate they can be used in the PAU's principal search
        scheme.
        
        Currently, our list of authenticators::
        
          >>> pau.authenticatorPlugins
          ('My Authenticator Plugin',)
        
        does not include a queriable authenticator. PAU cannot therefore provide any
        queriables::
        
          >>> list(pau.getQueriables())
          []
        
        Before we illustrate how an authenticator is used by the PAU to search for
        principals, we need to setup an adapter used by PAU::
        
          >>> provideAdapter(
          ...     authentication.authentication.QuerySchemaSearchAdapter,
          ...     provides=interfaces.IQueriableAuthenticator)
        
        This adapter delegates search responsibility to an authenticator, but prepends
        the PAU prefix to any principal IDs returned in a search.
        
        Next, we'll create a plugin that provides a search interface::
        
          >>> class QueriableAuthenticatorPlugin(MyAuthenticatorPlugin):
          ...
          ...     interface.implements(interfaces.IQuerySchemaSearch)
          ...
          ...     schema = None
          ...
          ...     def search(self, query, start=None, batch_size=None):
          ...         yield 'foo'
          ...
        
        and install it as a plugin::
        
          >>> plugin = QueriableAuthenticatorPlugin()
          >>> provideUtility(plugin,
          ...                provides=interfaces.IAuthenticatorPlugin,
          ...                name='Queriable')
          >>> pau.authenticatorPlugins += ('Queriable',)
        
        Now, the PAU provides a single queriable::
        
          >>> list(pau.getQueriables()) # doctest: +ELLIPSIS
          [('Queriable', ...QuerySchemaSearchAdapter object...)]
        
        We can use this queriable to search for our principal::
        
          >>> queriable = list(pau.getQueriables())[0][1]
          >>> list(queriable.search('not-used'))
          ['mypau_foo']
        
        Note that the resulting principal ID includes the PAU prefix. Were we to search
        the plugin directly::
        
          >>> list(plugin.search('not-used'))
          ['foo']
        
        The result does not include the PAU prefix. The prepending of the prefix is
        handled by the PluggableAuthenticationQueriable.
        
        
        Queryiable plugins can provide the ILocation interface. In this case the
        QuerySchemaSearchAdapter's __parent__ is the same as the __parent__ of the
        plugin::
        
          >>> import zope.location.interfaces
          >>> class LocatedQueriableAuthenticatorPlugin(QueriableAuthenticatorPlugin):
          ...
          ...     interface.implements(zope.location.interfaces.ILocation)
          ...
          ...     __parent__ = __name__ = None
          ...
          >>> import zope.site.hooks
          >>> site = zope.site.hooks.getSite()
          >>> plugin = LocatedQueriableAuthenticatorPlugin()
          >>> plugin.__parent__ = site
          >>> plugin.__name__ = 'localname'
          >>> provideUtility(plugin,
          ...                provides=interfaces.IAuthenticatorPlugin,
          ...                name='location-queriable')
          >>> pau.authenticatorPlugins = ('location-queriable',)
        
        We have one queriable again::
        
          >>> queriables = list(pau.getQueriables())
          >>> queriables  # doctest: +ELLIPSIS
          [('location-queriable', ...QuerySchemaSearchAdapter object...)]
        
        The queriable's __parent__ is the site as set above::
        
          >>> queriable = queriables[0][1]
          >>> queriable.__parent__ is site
          True
        
        If the queriable provides ILocation but is not actually locatable (i.e. the
        parent is None) the pau itself becomes the parent::
        
        
          >>> plugin = LocatedQueriableAuthenticatorPlugin()
          >>> provideUtility(plugin,
          ...                provides=interfaces.IAuthenticatorPlugin,
          ...                name='location-queriable-wo-parent')
          >>> pau.authenticatorPlugins = ('location-queriable-wo-parent',)
        
        We have one queriable again::
        
          >>> queriables = list(pau.getQueriables())
          >>> queriables  # doctest: +ELLIPSIS
          [('location-queriable-wo-parent', ...QuerySchemaSearchAdapter object...)]
        
        And the parent is the pau::
        
          >>> queriable = queriables[0][1]
          >>> queriable.__parent__  # doctest: +ELLIPSIS
          <zope.pluggableauth.authentication.PluggableAuthentication object ...>
          >>> queriable.__parent__ is pau
          True
        
        
        ================
        Principal Folder
        ================
        
        Principal folders contain principal-information objects that contain principal
        information. We create an internal principal using the `InternalPrincipal`
        class:
        
          >>> from zope.app.authentication.principalfolder import InternalPrincipal
          >>> p1 = InternalPrincipal('login1', '123', "Principal 1",
          ...     passwordManagerName="SHA1")
          >>> p2 = InternalPrincipal('login2', '456', "The Other One")
        
        and add them to a principal folder:
        
          >>> from zope.app.authentication.principalfolder import PrincipalFolder
          >>> principals = PrincipalFolder('principal.')
          >>> principals['p1'] = p1
          >>> principals['p2'] = p2
        
        Authentication
        --------------
        
        Principal folders provide the `IAuthenticatorPlugin` interface. When we
        provide suitable credentials:
        
          >>> from pprint import pprint
          >>> principals.authenticateCredentials({'login': 'login1', 'password': '123'})
          PrincipalInfo(u'principal.p1')
        
        We get back a principal id and supplementary information, including the
        principal title and description.  Note that the principal id is a concatenation
        of the principal-folder prefix and the name of the principal-information object
        within the folder.
        
        None is returned if the credentials are invalid:
        
          >>> principals.authenticateCredentials({'login': 'login1',
          ...                                     'password': '1234'})
          >>> principals.authenticateCredentials(42)
        
        Search
        ------
        
        Principal folders also provide the IQuerySchemaSearch interface.  This
        supports both finding principal information based on their ids:
        
          >>> principals.principalInfo('principal.p1')
          PrincipalInfo('principal.p1')
        
          >>> principals.principalInfo('p1')
        
        and searching for principals based on a search string:
        
          >>> list(principals.search({'search': 'other'}))
          [u'principal.p2']
        
          >>> list(principals.search({'search': 'OTHER'}))
          [u'principal.p2']
        
          >>> list(principals.search({'search': ''}))
          [u'principal.p1', u'principal.p2']
        
          >>> list(principals.search({'search': 'eek'}))
          []
        
          >>> list(principals.search({}))
          []
        
        If there are a large number of matches:
        
          >>> for i in range(20):
          ...     i = str(i)
          ...     p = InternalPrincipal('l'+i, i, "Dude "+i)
          ...     principals[i] = p
        
          >>> pprint(list(principals.search({'search': 'D'})))
          [u'principal.0',
           u'principal.1',
           u'principal.10',
           u'principal.11',
           u'principal.12',
           u'principal.13',
           u'principal.14',
           u'principal.15',
           u'principal.16',
           u'principal.17',
           u'principal.18',
           u'principal.19',
           u'principal.2',
           u'principal.3',
           u'principal.4',
           u'principal.5',
           u'principal.6',
           u'principal.7',
           u'principal.8',
           u'principal.9']
        
        We can use batching parameters to specify a subset of results:
        
          >>> pprint(list(principals.search({'search': 'D'}, start=17)))
          [u'principal.7', u'principal.8', u'principal.9']
        
          >>> pprint(list(principals.search({'search': 'D'}, batch_size=5)))
          [u'principal.0',
           u'principal.1',
           u'principal.10',
           u'principal.11',
           u'principal.12']
        
          >>> pprint(list(principals.search({'search': 'D'}, start=5, batch_size=5)))
          [u'principal.13',
           u'principal.14',
           u'principal.15',
           u'principal.16',
           u'principal.17']
        
        There is an additional method that allows requesting the principal id
        associated with a login id.  The method raises KeyError when there is
        no associated principal::
        
          >>> principals.getIdByLogin("not-there")
          Traceback (most recent call last):
          KeyError: 'not-there'
        
        If there is a matching principal, the id is returned::
        
          >>> principals.getIdByLogin("login1")
          u'principal.p1'
        
        Changing credentials
        --------------------
        
        Credentials can be changed by modifying principal-information objects:
        
          >>> p1.login = 'bob'
          >>> p1.password = 'eek'
        
          >>> principals.authenticateCredentials({'login': 'bob', 'password': 'eek'})
          PrincipalInfo(u'principal.p1')
        
          >>> principals.authenticateCredentials({'login': 'login1',
          ...                                     'password': 'eek'})
        
          >>> principals.authenticateCredentials({'login': 'bob',
          ...                                     'password': '123'})
        
        
        It is an error to try to pick a login name that is already taken:
        
          >>> p1.login = 'login2'
          Traceback (most recent call last):
          ...
          ValueError: Principal Login already taken!
        
        If such an attempt is made, the data are unchanged:
        
          >>> principals.authenticateCredentials({'login': 'bob', 'password': 'eek'})
          PrincipalInfo(u'principal.p1')
        
        Removing principals
        -------------------
        
        Of course, if a principal is removed, we can no-longer authenticate it:
        
          >>> del principals['p1']
          >>> principals.authenticateCredentials({'login': 'bob',
          ...                                     'password': 'eek'})
        
        
        ============
        Vocabularies
        ============
        
        The vocabulary module provides vocabularies for the authenticator plugins and
        the credentials plugins.
        
        The options should include the unique names of all of the plugins that provide
        the appropriate interface (interfaces.ICredentialsPlugin or
        interfaces.IAuthentiatorPlugin, respectively) for the current context-- which
        is expected to be a pluggable authentication utility, hereafter referred to as
        a PAU.
        
        These names may be for objects contained within the PAU ("contained
        plugins"), or may be utilities registered for the specified interface,
        found in the context of the PAU ("utility plugins").  Contained
        plugins mask utility plugins of the same name.  They also may be names
        currently selected in the PAU that do not actually have a
        corresponding plugin at this time.
        
        Here is a short example of how the vocabulary should work.  Let's say we're
        working with authentication plugins.  We'll create some faux
        authentication plugins, and register some of them as utilities and put
        others in a faux PAU.
        
            >>> from zope.app.authentication import interfaces
            >>> from zope import interface, component
            >>> class DemoPlugin(object):
            ...     interface.implements(interfaces.IAuthenticatorPlugin)
            ...     def __init__(self, name):
            ...         self.name = name
            ...
            >>> utility_plugins = dict(
            ...     (i, DemoPlugin(u'Plugin %d' % i)) for i in range(4))
            >>> contained_plugins = dict(
            ...     (i, DemoPlugin(u'Plugin %d' % i)) for i in range(1, 5))
            >>> sorted(utility_plugins.keys())
            [0, 1, 2, 3]
            >>> for p in utility_plugins.values():
            ...     component.provideUtility(p, name=p.name)
            ...
            >>> sorted(contained_plugins.keys()) # 1 will mask utility plugin 1
            [1, 2, 3, 4]
            >>> class DemoAuth(dict):
            ...     interface.implements(interfaces.IPluggableAuthentication)
            ...     def __init__(self, *args, **kwargs):
            ...         super(DemoAuth, self).__init__(*args, **kwargs)
            ...         self.authenticatorPlugins = (u'Plugin 3', u'Plugin X')
            ...         self.credentialsPlugins = (u'Plugin 4', u'Plugin X')
            ...
            >>> auth = DemoAuth((p.name, p) for p in contained_plugins.values())
        
            >>> @component.adapter(interface.Interface)
            ... @interface.implementer(component.IComponentLookup)
            ... def getSiteManager(context):
            ...     return component.getGlobalSiteManager()
            ...
            >>> component.provideAdapter(getSiteManager)
        
        We are now ready to create a vocabulary that we can use.  The context is
        our faux authentication utility, `auth`.
        
            >>> from zope.app.authentication import vocabulary
            >>> vocab = vocabulary.authenticatorPlugins(auth)
        
        Iterating over the vocabulary results in all of the terms, in a relatively
        arbitrary order of their names.  (This vocabulary should typically use a
        widget that sorts values on the basis of localized collation order of the
        term titles.)
        
            >>> [term.value for term in vocab] # doctest: +NORMALIZE_WHITESPACE
            [u'Plugin 0', u'Plugin 1', u'Plugin 2', u'Plugin 3', u'Plugin 4',
             u'Plugin X']
        
        Similarly, we can use `in` to test for the presence of values in the
        vocabulary.
        
            >>> ['Plugin %s' % i in vocab for i in range(-1, 6)]
            [False, True, True, True, True, True, False]
            >>> 'Plugin X' in vocab
            True
        
        The length reports the expected value.
        
            >>> len(vocab)
            6
        
        One can get a term for a given value using `getTerm()`; its token, in
        turn, should also return the same effective term from `getTermByToken`.
        
            >>> values = ['Plugin 0', 'Plugin 1', 'Plugin 2', 'Plugin 3', 'Plugin 4',
            ...           'Plugin X']
            >>> for val in values:
            ...     term = vocab.getTerm(val)
            ...     assert term.value == val
            ...     term2 = vocab.getTermByToken(term.token)
            ...     assert term2.token == term.token
            ...     assert term2.value == val
            ...
        
        The terms have titles, which are message ids that show the plugin title or id
        and whether the plugin is a utility or just contained in the auth utility.
        We'll give one of the plugins a dublin core title just to show the
        functionality.
        
            >>> import zope.dublincore.interfaces
            >>> class ISpecial(interface.Interface):
            ...     pass
            ...
            >>> interface.directlyProvides(contained_plugins[1], ISpecial)
            >>> class DemoDCAdapter(object):
            ...     interface.implements(
            ...         zope.dublincore.interfaces.IDCDescriptiveProperties)
            ...     component.adapts(ISpecial)
            ...     def __init__(self, context):
            ...         pass
            ...     title = u'Special Title'
            ...
            >>> component.provideAdapter(DemoDCAdapter)
        
        We need to regenerate the vocabulary, since it calculates all of its data at
        once.
        
            >>> vocab = vocabulary.authenticatorPlugins(auth)
        
        Now we'll check the titles.  We'll have to translate them to see what we
        expect.
        
            >>> from zope import i18n
            >>> import pprint
            >>> pprint.pprint([i18n.translate(term.title) for term in vocab])
            [u'Plugin 0 (a utility)',
             u'Special Title (in contents)',
             u'Plugin 2 (in contents)',
             u'Plugin 3 (in contents)',
             u'Plugin 4 (in contents)',
             u'Plugin X (not found; deselecting will remove)']
        
        credentialsPlugins
        ------------------
        
        For completeness, we'll do the same review of the credentialsPlugins.
        
            >>> class DemoPlugin(object):
            ...     interface.implements(interfaces.ICredentialsPlugin)
            ...     def __init__(self, name):
            ...         self.name = name
            ...
            >>> utility_plugins = dict(
            ...     (i, DemoPlugin(u'Plugin %d' % i)) for i in range(4))
            >>> contained_plugins = dict(
            ...     (i, DemoPlugin(u'Plugin %d' % i)) for i in range(1, 5))
            >>> for p in utility_plugins.values():
            ...     component.provideUtility(p, name=p.name)
            ...
            >>> auth = DemoAuth((p.name, p) for p in contained_plugins.values())
            >>> vocab = vocabulary.credentialsPlugins(auth)
        
        Iterating over the vocabulary results in all of the terms, in a relatively
        arbitrary order of their names.  (This vocabulary should typically use a
        widget that sorts values on the basis of localized collation order of the term
        titles.) Similarly, we can use `in` to test for the presence of values in the
        vocabulary. The length reports the expected value.
        
            >>> [term.value for term in vocab] # doctest: +NORMALIZE_WHITESPACE
            [u'Plugin 0', u'Plugin 1', u'Plugin 2', u'Plugin 3', u'Plugin 4',
             u'Plugin X']
            >>> ['Plugin %s' % i in vocab for i in range(-1, 6)]
            [False, True, True, True, True, True, False]
            >>> 'Plugin X' in vocab
            True
            >>> len(vocab)
            6
        
        One can get a term for a given value using `getTerm()`; its token, in
        turn, should also return the same effective term from `getTermByToken`.
        
            >>> values = ['Plugin 0', 'Plugin 1', 'Plugin 2', 'Plugin 3', 'Plugin 4',
            ...           'Plugin X']
            >>> for val in values:
            ...     term = vocab.getTerm(val)
            ...     assert term.value == val
            ...     term2 = vocab.getTermByToken(term.token)
            ...     assert term2.token == term.token
            ...     assert term2.value == val
            ...
        
        The terms have titles, which are message ids that show the plugin title or id
        and whether the plugin is a utility or just contained in the auth utility.
        We'll give one of the plugins a dublin core title just to show the
        functionality. We need to regenerate the vocabulary, since it calculates all
        of its data at once. Then we'll check the titles.  We'll have to translate
        them to see what we expect.
        
            >>> interface.directlyProvides(contained_plugins[1], ISpecial)
            >>> vocab = vocabulary.credentialsPlugins(auth)
            >>> pprint.pprint([i18n.translate(term.title) for term in vocab])
            [u'Plugin 0 (a utility)',
             u'Special Title (in contents)',
             u'Plugin 2 (in contents)',
             u'Plugin 3 (in contents)',
             u'Plugin 4 (in contents)',
             u'Plugin X (not found; deselecting will remove)']
        
        
        =======
        Changes
        =======
        
        3.9 (2010-10-18)
        ----------------
        
        * Move concrete IAuthenticatorPlugin implementations to
          zope.pluggableauth.plugins. Leave backwards compatibility imports.
        
        * Use zope.formlib throughout to lift the dependency on zope.app.form. As it
          turns out, zope.app.form is still a indirect test dependency though.
        
        3.8.0 (2010-09-25)
        ------------------
        
        * Using python's ``doctest`` module instead of deprecated
          ``zope.testing.doctest[unit]``.
        
        * Moved the following views from `zope.app.securitypolicy` here, to inverse
          dependency between these two packages, as `zope.app.securitypolicy`
          deprecated in ZTK 1.0:
        
          - ``@@grant.html``
          - ``@@AllRolePermissions.html``
          - ``@@RolePermissions.html``
          - ``@@RolesWithPermission.html``
        
        3.7.1 (2010-02-11)
        ------------------
        
        * Using the new `principalfactories.zcml` file, from ``zope.pluggableauth``,
          to avoid duplication errors, in the adapters registration.
        
        3.7.0 (2010-02-08)
        ------------------
        
        * The Pluggable Authentication utility has been severed and released
          in a standalone package: `zope.pluggableauth`. We are now using this
          new package, providing backward compatibility imports to assure a
          smooth transition.
        
        3.6.2 (2010-01-05)
        ------------------
        
        * Fix tests by using zope.login, and require new zope.publisher 3.12.
        
        3.6.1 (2009-10-07)
        ------------------
        
        * Fix ftesting.zcml due to ``zope.securitypolicy`` update.
        
        * Don't use ``zope.app.testing.ztapi`` in tests, use zope.component's
          testing functions instead.
        
        * Fix functional tests and stop using port 8081. Redirecting to
          different port without trusted flag is not allowed.
        
        3.6.0 (2009-03-14)
        ------------------
        
        * Separate the presentation template and camefrom/redirection logic for the
          ``loginForm.html`` view. Now the logic is contained in the
          ``zope.app.authentication.browser.loginform.LoginForm`` class.
        
        * Fix login form redirection failure in some cases with Python 2.6.
        
        * Use the new ``zope.authentication`` package instead of ``zope.app.security``.
        
        * The "Password Manager Names" vocabulary and simple password manager registry
          were moved to the ``zope.password`` package.
        
        * Remove deprecated code.
        
        3.5.0 (2009-03-06)
        ------------------
        
        * Split password manager functionality off to the new ``zope.password``
          package. Backward-compatibility imports are left in place.
        
        * Use ``zope.site`` instead of ``zope.app.component``. (Browser code still
          needs ``zope.app.component`` as it depends on view classes of this
          package.)
        
        3.5.0a2 (2009-02-01)
        --------------------
        
        * Make old encoded passwords really work.
        
        3.5.0a1 (2009-01-31)
        --------------------
        
        * Use ``zope.container`` instead of ``zope.app.container``. (Browser code
          still needs ``zope.app.container`` as it depends on view classes of this
          package.)
        
        * Encoded passwords are now stored with a prefix ({MD5}, {SHA1},
          {SSHA}) indicating the used encoding schema. Old (encoded) passwords
          can still be used.
        
        * Add an SSHA password manager that is compatible with standard LDAP
          passwords. As this encoding gives better security agains dictionary
          attacks, users are encouraged to switch to this new password schema.
        
        * InternalPrincipal now uses SSHA password manager by default.
        
        3.4.4 (2008-12-12)
        ------------------
        
        * Depend on zope.session instead of zope.app.session. The first one
          currently has all functionality we need.
        * Fix deprecation warnings for ``md5`` and ``sha`` on Python 2.6.
        
        3.4.3 (2008-08-07)
        ------------------
        
        * No changes. Retag for correct release on PyPI.
        
        3.4.2 (2008-07-09)
        -------------------
        
        * Make it compatible with zope.app.container 3.6.1 and 3.5.4 changes,
          Changed ``super(BTreeContainer, self).__init__()`` to
          ``super(GroupFolder, self).__init__()`` in ``GroupFolder`` class.
        
        3.4.1 (2007-10-24)
        ------------------
        
        * Avoid deprecation warning.
        
        3.4.0 (2007-10-11)
        ------------------
        
        * Updated package meta-data.
        
        3.4.0b1 (2007-09-27)
        --------------------
        
        * First release independent of Zope.
        
Keywords: zope3 authentication pluggable principal group
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Zope Public License
Classifier: Programming Language :: Python
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Framework :: Zope3