Tres Seaver
2010-10-01 aecb0afe36378572ff9bb15027e68f71e1f030af
commit | author | age
7141c7 1 repoze.who Changelog
TS 2 ====================
aa8755 3
f80021 4 After 2.0a2 (unreleased)
8e245d 5 ------------------------
f80021 6
a446d6 7 - Deprecated the following plugins, moving their modules, tests, and docs
ff604c 8   to a new project, ``repoze.who.deprecatedplugins``:
a446d6 9
TS 10   - ``repoze.who.plugins.cookie.InsecureCookiePlugin``
11
12   - ``repoze.who.plugins.form.FormPlugin
13
14   - ``repoze.who.plugins.form.RedirectingFormPlugin
15
16 - Made the ``repoze.who.plugins.cookie.InsecureCookiePlugin`` take a
76e951 17   ``charset`` argument, and use to to encode / decode login and password.
TS 18   See http://bugs.repoze.org/issue155
19
a446d6 20 - Updated ``repoze.who.restrict`` to return headers as a list, to keep
TS 21   ``wsgiref`` from complaining.
5b6365 22
a446d6 23 - Helped default request classifier cope with xml submissions with an
6b7b34 24   explicit charset defined: http://bugs.repoze.org/issue145 (Lorenzo
CM 25   M. Catucci)
26
a446d6 27 - Corrected the handling of type and subtype when matching an XML post
6b7b34 28   to ``xmlpost`` in the default classifier, which, according to RFC
CM 29   2045, must be matched case-insensitively:
30   http://bugs.repoze.org/issue145 (Lorenzo M. Catucci)
31
a349a2 32 - Added ``repoze.who.config:make_api_factory_with_config``, a convenience
TS 33   method for applications which want to set up their own API Factory from
34   a configuration file.
35   
f80021 36 - Fixed example call to ``repoze.who.config:make_middleware_with_config``
TS 37   (added missing ``global_config`` argument).  See
38   http://bugs.repoze.org/issue114
39
c186ae 40 2.0a2 (2010-03-25)
TS 41 ------------------
52bc23 42
TS 43 Bugs Fixed
44 ~~~~~~~~~~
45
46 - Fixed failure to pass substution values in log message string formatting
47   for ``repoze.who.api:API.challenge``.  Fix included adding tests for all
48   logging done by the API object.  See http://bugs.repoze.org/issue122
49
50 Backward Incompatibilities
51 ~~~~~~~~~~~~~~~~~~~~~~~~~~
52
53 - Adjusted logging level for some lower-level details from ``info``
54   to ``debug``.
55
56
e25b84 57 2.0a1 (2010-02-24)
993216 58 ------------------
TS 59
b4b8ee 60 Features
TS 61 ~~~~~~~~
62cd25 62
c9c1c6 63 - Restored the ability to create the middleware using the old ``classifier``
TS 64   argument.  That argument is now a deprecated-but-will-work-forever alias for
65   ``request_classifier``.
cbc983 66
b0f81f 67 - The ``auth_tkt`` plugin now implements the ``IAuthenticator`` interface,
TS 68   and should normally be used both as an ``IIdentifier`` and an
69   ``IAuthenticator``.
70
993216 71 - Factored out the API of the middleware object to make it useful from
TS 72   within the application.  Applications using ``repoze.who``` now fall into
73   one of three catgeories:
74
75   - "middleware-only" applications are configured with middleware, and
76     use either ``REMOTE_USER`` or ``repoze.who.identity`` from the environment
77     to determing the authenticated user.
78
79   - "bare metal" applications use no ``repoze.who`` middleware at all:
80     instead, they configure and an ``APIFactory`` object at startup, and
81     use it to create an ``API`` object when needed on a per-request basis.
82
83   - "hybrid" applications are configured with ``repoze.who`` middleware,
84     but use a new library function to fetch the ``API`` object from the
c9c1c6 85     environ, e.g. to permit calling ``remember`` after a signup or successful
TS 86     login.
993216 87
b4b8ee 88 Bugs Fixed
TS 89 ~~~~~~~~~~
90
91 - Fix http://bugs.repoze.org/issue102: when no challengers existed,
92   logging would cause an exception.
93
94 - Remove ``ez_setup.py`` and dependency on it in setup.py (support
95   distribute).
96
97 Backward Incompatibilities
98 ~~~~~~~~~~~~~~~~~~~~~~~~~~
99
b213af 100 - The middleware used to allow identifier plugins to "pre-authenticate"
TS 101   an identity.  This feature is no longer supported: the ``auth_tkt`` 
102   plugin, which used to use the feature, is now configured to work as
c9c1c6 103   an authenticator plugin (as well as an identifier).
b213af 104
b4b8ee 105 - The ``repoze.who.middleware:PluggableAuthenticationMiddleware`` class
TS 106   no longer has the following (non-API) methods (now made API methods
107   of the ``repoze.who.api:API`` class):
108
109   - ``add_metadata``
110   - ``authenticate``
111   - ``challenge``
112   - ``identify``
113
114 - The following (non-API) functions moved from ``repoze.who.middleware`` to
115   ``repoze.who.api``:
116   
117   - ``make_registries``
118   - ``match_classification``
119   - ``verify``
120
121
060054 122 1.0.18 (2009-11-05)
TS 123 -------------------
798feb 124
TS 125 - Issue #104:  AuthTkt plugin was passing an invalid cookie value in
126   headers from ``forget``, and was not setting the ``Max-Age`` and 
127   ``Expires`` attributes of those cookies.
128
b4b8ee 129
6e136f 130 1.0.17 (2009-11-05)
TS 131 -------------------
e0d138 132
TS 133 - Fixed the ``repoze.who.plugins.form.make_plugin`` factory's ``formcallable``
134   argument handling, to allow passing in a dotted name (e.g., from a config
135   file).
136
b4b8ee 137
6b15ee 138 1.0.16 (2009-11-04)
028e4d 139 -------------------
1ec83d 140
8dd881 141 - Exposed ``formcallable`` argument for ``repoze.who.plugins.form.FormPlugin``
TS 142   to the callers of the ``repoze.who.plugins.form.make_plugin`` factory.
143   Thanks to Roland Hedburg for the report.
21a9c5 144
8dd881 145 - Fixed an issue that caused the following symptom when using the
TS 146   ini configuration parser::
147
148    TypeError: _makePlugin() got multiple values for keyword argument 'name'
21a9c5 149
CM 150   See http://bugs.repoze.org/issue92 for more details.  Thanks to vaab
151   for the bug report and initial fix.
152
1ec83d 153
7141c7 154 1.0.15 (2009-06-25)
TS 155 -------------------
299b4c 156
a14163 157 - If the form post value ``max_age`` exists while in the ``identify``
CM 158   method is handling the ``login_handler_path``, pass the max_age
159   value in the returned identity dictionary as ``max_age``.  See the
160   below bullet point for why.
161
299b4c 162 - If the ``identity`` dict passed to the ``auth_tkt`` ``remember``
CM 163   method contains a ``max_age`` key with a string (or integer) value,
164   treat it as a cue to set the ``Max-Age`` and ``Expires`` headers in
165   the returned cookies.  The cookie ``Max-Age`` is set to the value
166   and the ``Expires`` is computed from the current time.
167
7141c7 168
TS 169 1.0.14 (2009-06-17)
170 -------------------
9318dd 171
1810b2 172 - Fix test breakage on Windows.  See http://bugs.repoze.org/issue79 .
TS 173
00a6d9 174 - Documented issue with using ``include_ip`` setting in the ``auth_tkt``
TS 175   plugin.  See http://bugs.repoze.org/issue81 .
176
0dd808 177 - Added 'passthrough_challenge_decider', which avoids re-challenging 401
TS 178   responses which have been "pre-challenged" by the application.
179
9318dd 180 - One-hundred percent unit test coverage.
TS 181
a6f6dc 182 - Add ``timeout`` and ``reissue_time`` arguments to the auth_tkt
CM 183   identifier plugin, courtesty of Paul Johnston.
184
185 - Add a ``userid_checker`` argument to the auth_tkt identifier plugin,
186   courtesty of Gustavo Narea.
187
188   If ``userid_checker`` is provided, it must be a dotted Python name
189   that resolves to a function which accepts a userid and returns a
190   boolean True or False, indicating whether that user exists in a
191   database.  This is a workaround.  Due to a design bug in repoze.who,
192   the only way who can check for user existence is to use one or more
193   IAuthenticator plugin ``authenticate`` methods.  If an
194   IAuthenticator's ``authenticate`` method returns true, it means that
195   the user exists.  However most IAuthenticator plugins expect *both*
196   a username and a password, and will return False unconditionally if
197   both aren't supplied.  This means that an authenticator can't be
198   used to check if the user "only" exists.  The identity provided by
199   an auth_tkt does not contain a password to check against.  The
200   actual design bug in repoze.who is this: when a user presents
201   credentials from an auth_tkt, he is considered "preauthenticated".
202   IAuthenticator.authenticate is just never called for a
203   "preauthenticated" identity, which works fine, but it means that the
204   user will be considered authenticated even if you deleted the user's
205   record from whatever database you happen to be using.  However, if
206   you use a userid_checker, you can ensure that a user exists for the
207   auth_tkt supplied userid.  If the userid_checker returns False, the
208   auth_tkt credentials are considered "no good".
209
7141c7 210
TS 211 1.0.13 (2009-04-24)
212 -------------------
64ba13 213
TS 214 - Added a paragraph to ``IAuthenticator`` docstring, documenting that plugins
215   are allowed to add keys to the ``identity`` dictionary (e.g., to save a
ced7bd 216   second database query in an ``IMetadataProvider`` plugin).
64ba13 217
08b2ae 218 - Patch supplied for issue #71 (http://bugs.repoze.org/issue71)
CM 219   whereby a downstream app can return a generator, relying on an
220   upstream component to call start_response.  We do this because the
221   challenge decider needs the status and headers to decide what to do.
222
56d0c5 223
7141c7 224 1.0.12 (2009-04-19)
TS 225 -------------------
56d0c5 226 - auth_tkt plugin tried to append REMOTE_USER_TOKENS data to
CM 227   existing tokens data returned by auth_tkt.parse_tkt; this was
228   incorrect; just overwrite.
0ee58d 229
TS 230 - Extended auth_tkt plugin factory to allow passing secret in a separate
231   file from the main config file.  See http://bugs.repoze.org/issue40 .
232
7141c7 233
TS 234 1.0.11 (2009-04-10)
235 -------------------
afbbcd 236
8c20ba 237 - Fix auth_tkt plugin; cookie values are now quoted, making it possible
CM 238   to put spaces and other whitespace, etc in usernames. (thanks to Michael
95736b 239   Pedersen).
8c20ba 240
afbbcd 241 - Fix corner case issue of an exception raised when attempting to log
CM 242   when there are no identifiers or authenticators.
243
7141c7 244
TS 245 1.0.10 (2009-01-23)
246 -------------------
7b931d 247
CM 248 - The RedirectingFormPlugin now passes along SetCookie headers set
249   into the response by the application within the NotFound response
250   (fixes TG2 "flash" issue).
251
7141c7 252
TS 253 1.0.9 (2008-12-18)
254 ------------------
30ab69 255
9238cd 256 - The RedirectingFormPlugin now attempts to find a header named
CM 257   ``X-Authentication-Failure-Reason`` among the response headers set
258   by the application when a challenge is issued.  If a value for this
259   header exists (and is non-blank), the value is attached to the
260   redirect URL's query string as the ``reason`` parameter (or a
261   user-settable key).  This makes it possible for downstream
262   applications to issue a response that initiates a challenge with
263   this header and subsequently display the reason in the login form
264   rendered as a result of the challenge.
30ab69 265
7141c7 266
TS 267 1.0.8 (2008-12-13)
268 ------------------
186ff6 269
9238cd 270 - The ``PluggableAuthenticationMiddleware`` constructor accepts a
CM 271   ``log_stream`` argument, which is typically a file.  After this
272   release, it can also be a PEP 333 ``Logger`` instance; if it is a
273   PEP 333 ``Logger`` instance, this logger will be used as the
274   repoze.who logger (instead of one being constructed by the
275   middleware, as was previously always the case).  When the
276   ``log_stream`` argument is a PEP 333 Logger object, the
277   ``log_level`` argument is ignored.
186ff6 278
7141c7 279
TS 280 1.0.7 (2008-08-28)
281 ------------------
37de44 282
9238cd 283 - ``repoze.who`` and ``repoze.who.plugins`` were not added to the
CM 284   ``namespace_packages`` list in setup.py, potentially making 1.0.6 a
285   brownbag release, given that making these packages namespace
286   packages was the only reason for its release.
37de44 287
7141c7 288
TS 289 1.0.6 (2008-08-28)
290 ------------------
facdf8 291
9238cd 292 - Make repoze.who and repoze.who.plugins into namespace packages
CM 293   mainly so we can allow plugin authors to distribute packages in the
294   repoze.who.plugins namespace.
facdf8 295
7141c7 296
TS 297 1.0.5 (2008-08-23)
298 ------------------
519300 299
9238cd 300 - Fix auth_tkt plugin to set the same cookies in its ``remember``
CM 301   method that it does in its ``forget`` method.  Previously, logging
302   out and relogging back in to a site that used auth_tkt identifier
303   plugin was slightly dicey and would only work sometimes.
facdf8 304
9238cd 305 - The FormPlugin plugin has grown a redirect-on-unauthorized feature.
CM 306   Any response from a downstream application that causes a challenge
307   and includes a Location header will cause a redirect to the value of
308   the Location header.
dee08c 309
7141c7 310
TS 311 1.0.4 (2008-08-22)
312 ------------------
b95a59 313
9238cd 314 - Added a key to the '[general]' config section: ``remote_user_key``.
CM 315   If you use this key in the config file, it tells who to 1) not
316   perform any authentication if it exists in the environment during
317   ingress and 2) to set the key in the environment for the downstream
318   app to use as the REMOTE_USER variable.  The default is
319   ``REMOTE_USER``.
b95a59 320
9238cd 321 - Using unicode user ids in combination with the auth_tkt plugin would
CM 322   cause problems under mod_wsgi.
55dc7a 323
9238cd 324 - Allowed 'cookie_path' argument to InsecureCookiePlugin (and config
CM 325   constructor).  Thanks to Gustavo Narea.
55dc7a 326
7141c7 327
TS 328 1.0.3 (2008-08-16)
329 ------------------
f693fe 330
9238cd 331 - A bug in the middleware's ``authenticate`` method made it impossible
CM 332   to authenticate a user with a userid that was null (e.g. 0, False),
333   which are valid identifiers.  The only invalid userid is now None.
c7e12d 334
9238cd 335 - Applied patch from Olaf Conradi which logs an error when an invalid
CM 336   filename is passed to the HTPasswdPlugin.
c7e12d 337
7141c7 338
TS 339 1.0.2 (2008-06-16)
340 ------------------
cad90d 341
9238cd 342 - Fix bug found by Chris Perkins: the auth_tkt plugin's "remember"
CM 343   method didn't handle userids which are Python "long" instances
344   properly.  Symptom: TypeError: cannot concatenate 'str' and 'long'
345   objects in "paste.auth.auth_tkt".
a2c030 346
9238cd 347 - Added predicate-based "restriction" middleware support
CM 348   (repoze.who.restrict), allowing configuratio-driven authorization as
349   a WSGI filter.  One example predicate, 'authenticated_predicate', is
350   supplied, which requires that the user be authenticated either via
351   'REMOTE_USER' or via 'repoze.who.identity'.  To use the filter to
352   restrict access::
cad90d 353
TS 354      [filter:authenticated_only]
355      use = egg:repoze.who#authenticated
356
357    or::
358
359      [filter:some_predicate]
360      use = egg:repoze.who#predicate
361      predicate = my.module:some_predicate
362      some_option = a value
363
7141c7 364
TS 365 1.0.1 (2008-05-24)
366 ------------------
8199a1 367
9238cd 368 - Remove dependency-link to dist.repoze.org to prevent easy_install
CM 369   from inserting that path into its search paths (the dependencies are
370   available from PyPI).
8199a1 371
7141c7 372
TS 373 1.0 (2008-05-04)
374 -----------------
419946 375
9238cd 376 - The plugin at plugins.form.FormPlugin didn't redirect properly after
CM 377   collecting identification information.  Symptom: a downstream app
378   would receive a POST request with a blank body, which would
379   sometimes result in a Bad Request error.
f39349 380
9238cd 381 - Fixed interface declarations of
CM 382   'classifiers.default_request_classifier' and
383   'classifiers.default_password_compare'.
515c69 384
9238cd 385 - Added actual config-driven middleware factory,
CM 386   'config.make_middleware_with_config'
515c69 387
9238cd 388 - Removed fossilized 'who_conf' argument from plugin factory functions.
515c69 389
7141c7 390 - Added ConfigParser-based WhoConfig, implementing the spec outlined at
9238cd 391   http://www.plope.com/static/misc/sphinxtest/intro.html#middleware-configuration-via-config-file,
CM 392   with the following changes:
419946 393
7141c7 394   - "Bare" plugins (requiring no configuration options) may be specified
419946 395      as either egg entry points (e.g., 'egg:distname#entry_point_name') or
TS 396      as dotted-path-with-colon (e.g., 'dotted.name:object_id').
397
7141c7 398   - Therefore, the separator between a plugin and its classifier is now
TS 399     a semicolon, rather than a colon. E.g.::
419946 400
TS 401      [plugins:id_plugin]
402      use = egg:another.package#identify_with_frobnatz
403      frobnatz = baz
404
405      [identifiers]
406      plugins =
407        egg:my.egg#identify;browser
408        dotted.name:identifier
409        id_plugin
410
7141c7 411
779caf 412 0.9.1 (2008-04-27)
7141c7 413 ------------------
779caf 414
9238cd 415 - Fix auth_tkt plugin to be able to encode and decode integer user
CM 416   ids.
779caf 417
7141c7 418
88e646 419 0.9 (2008-04-01)
7141c7 420 ----------------
88e646 421
9238cd 422 - Fix bug introduced in FormPlugin in 0.8 release (rememberer headers
CM 423   not set).
88e646 424
9238cd 425 - Add PATH_INFO to started and ended log info.
d9f046 426
9238cd 427 - Add a SQLMetadataProviderPlugin (in plugins/sql).
d9f046 428
9238cd 429 - Change constructor of SQLAuthenticatorPlugin: it now accepts only
CM 430   "query", "conn_factory", and "compare_fn".  The old constructor
431   accepted a DSN, but some database systems don't use DBAPI DSNs.  The
432   new constructor accepts no DSN; the conn_factory is assumed to do
433   all the work to make a connection, including knowing the DSN if one
434   is required.  The "conn_factory" should return something that, when
435   called with no arguments, returns a database connection.
d9f046 436
9238cd 437 - The "make_plugin" helper in plugins/sql has been renamed
CM 438   "make_authenticator_plugin".  When called, this helper will return a
439   SQLAuthenticatorPlugin.  A bit of helper logic in the
440   "make_authenticator_plugin" allows a connection factory to be
441   computed.  The top-level callable referred to by conn_factory in
442   this helper should return a function that, when called with no
443   arguments, returns a datbase connection.  The top-level callable
444   itself is called with "who_conf" (global who configuration) and any
445   number of non-top-level keyword arguments as they are passed into
446   the helper, to allow for a DSN or URL or whatever to be passed in.
d9f046 447
9238cd 448 - A "make_metatata_plugin" helper has been added to plugins/sql. When
CM 449   called, this will make a SQLMetadataProviderPlugin.  See the
450   implementation for details.  It is similar to the
451   "make_authenticator_plugin" helper.
d9f046 452
7141c7 453
cbe4e3 454 0.8 (2008-03-27)
7141c7 455 ----------------
b5a331 456
9238cd 457 - Add a RedirectingFormIdentifier plugin.  This plugin is willing to
CM 458   redirect to an external (or downstream application) login form to
459   perform identification.  The external login form must post to the
460   "login_handler_path" of the plugin (optimally with a "came_from"
461   value to tell the plugin where to redirect the response to if the
462   authentication works properly).  The "logout_handler_path" of this
463   plugin can be visited to perform a logout.  The "came_from" value
464   also works there.
a400b0 465
9238cd 466 - Identifier plugins are now permitted to set a key in the environment
CM 467   named 'repoze.who.application' on ingress (in 'identify').  If an
468   identifier plugin does so, this application is used instead of the
469   "normal" downstream application.  This feature was added to more
470   simply support the redirecting form identifier plugin.
a400b0 471
7141c7 472
a400b0 473 0.7 (2008-03-26)
7141c7 474 ----------------
a400b0 475
9238cd 476 - Change the IMetadataProvider interface: this interface used to have
CM 477   a "metadata" method which returned a dictionary.  This method is not
478   part of that API anymore.  It's been replaced with an "add_metadata"
479   method which has the signature::
b5a331 480
CM 481     def add_metadata(environ, identity):
482         """
483         Add metadata to the identity (which is a dictionary)
484         """
485
486    The return value is ignored.  IMetadataProvider plugins are now
487    assumed to be responsible for 'scribbling' directly on the identity
488    that is passed in (it's a dictionary).  The user id can always be
489    retrieved from the identity via identity['repoze.who.userid'] for
490    metadata plugins that rely on that value.
491
7141c7 492
a400b0 493 0.6 (2008-03-20)
7141c7 494 ----------------
e35c64 495
9238cd 496 - Renaming: repoze.pam is now repoze.who
cb5426 497
9238cd 498 - Bump ez_setup.py version.
e35c64 499
9238cd 500 - Add IMetadataProvider plugin type.  Chris says 'Whit rules'.
fa9581 501
7141c7 502
3b67e9 503 0.5 (2008-03-09)
7141c7 504 ----------------
db4cf5 505
9238cd 506 - Allow "remote user key" (default: REMOTE_USER) to be overridden
CM 507   (pass in remote_user_key to middleware constructor).
db4cf5 508
9238cd 509 - Allow form plugin to override the default form.
db4cf5 510
9238cd 511 - API change: IIdentifiers are no longer required to put both 'login'
CM 512   and 'password' in a returned identity dictionary.  Instead, an
513   IIdentifier can place arbitrary key/value pairs in the identity
514   dictionary (or return an empty dictionary).
40a968 515
9238cd 516 - API return value change: the "failure" identity which IIdentifiers
CM 517   return is now None rather than an empty dictionary.
40a968 518
9238cd 519 - The IAuthenticator interface now specifies that IAuthenticators must
CM 520   not raise an exception when evaluating an identity that does not
521   have "expected" key/value pairs (e.g. when an IAuthenticator that
522   expects login and password inspects an identity returned by an
523   IP-based auth system which only puts the IP address in the
524   identity); instead they fail gracefully by returning None.
40a968 525
9238cd 526 - Add (cookie) "auth_tkt" identification plugin.
a5b033 527
9238cd 528 - Stamp identity dictionaries with a userid by placing a key named
CM 529   'repoze.pam.userid' into the identity for each authenticated
530   identity.
a5b033 531
9238cd 532 - If an IIdentifier plugin inserts a 'repoze.pam.userid' key into the
CM 533   identity dictionary, consider this identity "preauthenticated".  No
534   authenticator plugins will be asked to authenticate this identity.
535   This is designed for things like the recently added auth_tkt plugin,
536   which embeds the user id into the ticket.  This effectively alllows
537   an IIdentifier plugin to become an IAuthenticator plugin when
538   breaking apart the responsibility into two separate plugins is
539   "make-work".  Preauthenticated identities will be selected first
540   when deciding which identity to use for any given request.
a5b033 541
9238cd 542 - Insert a 'repoze.pam.identity' key into the WSGI environment on
CM 543   ingress if an identity is found.  Its value will be the identity
544   dictionary related to the identity selected by repoze.pam on
545   ingress.  Downstream consumers are allowed to mutate this
546   dictionary; this value is passed to "remember" and "forget", so its
547   main use is to do a "credentials reset"; e.g. a user has changed his
548   username or password within the application, but we don't want to
549   force him to log in again after he does so.
a5b033 550
7141c7 551
247f34 552 0.4 (03-07-2008)
7141c7 553 ----------------
247f34 554
9238cd 555 - Allow plugins to specify a classifiers list per interface (instead
CM 556   of a single classifiers list per plugin).
247f34 557
7141c7 558
fb510d 559 0.3 (03-05-2008)
7141c7 560 ----------------
fb510d 561
9238cd 562 - Make SQLAuthenticatorPlugin's default_password_compare use hexdigest
CM 563   sha instead of base64'ed binary sha for simpler conversion.
fb510d 564
7141c7 565
196bc2 566 0.2 (03-04-2008)
7141c7 567 ----------------
196bc2 568
9238cd 569 - Added SQLAuthenticatorPlugin (see plugins/sql.py).
196bc2 570
7141c7 571
318832 572 0.1 (02-27-2008)
7141c7 573 ----------------
318832 574
9238cd 575 - Initial release (no configuration file support yet).