~sambuddhabasu1/mailman/fix_mailman_run_error

« back to all changes in this revision

Viewing changes to src/mailman/rest/docs/users.rst

  • Committer: Barry Warsaw
  • Date: 2015-03-03 02:34:09 UTC
  • mfrom: (7299.1.1 mailman_trunk)
  • Revision ID: barry@list.org-20150303023409-g7qqejbizzouc85k
Doc fixes given by Abhilash Raj.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
=====
 
2
Users
 
3
=====
 
4
 
 
5
The REST API can be used to add and remove users, add and remove user
 
6
addresses, and change their preferred address, password, or name.  The API can
 
7
also be used to verify a user's password.
 
8
 
 
9
Users are different than members; the latter represents an email address
 
10
subscribed to a specific mailing list.  Users are just people that Mailman
 
11
knows about.
 
12
 
 
13
There are no users yet.
 
14
 
 
15
    >>> dump_json('http://localhost:9001/3.0/users')
 
16
    http_etag: "..."
 
17
    start: 0
 
18
    total_size: 0
 
19
 
 
20
Anne is added, with an email address.  Her user record gets a `user_id`.
 
21
 
 
22
    >>> from zope.component import getUtility
 
23
    >>> from mailman.interfaces.usermanager import IUserManager
 
24
    >>> user_manager = getUtility(IUserManager)
 
25
    >>> anne = user_manager.create_user('anne@example.com', 'Anne Person')
 
26
    >>> transaction.commit()
 
27
    >>> int(anne.user_id.int)
 
28
    1
 
29
 
 
30
Anne's user record is returned as an entry into the collection of all users.
 
31
 
 
32
    >>> dump_json('http://localhost:9001/3.0/users')
 
33
    entry 0:
 
34
        created_on: 2005-08-01T07:49:23
 
35
        display_name: Anne Person
 
36
        http_etag: "..."
 
37
        self_link: http://localhost:9001/3.0/users/1
 
38
        user_id: 1
 
39
    http_etag: "..."
 
40
    start: 0
 
41
    total_size: 1
 
42
 
 
43
A user might not have a display name, in which case, the attribute will not be
 
44
returned in the REST API.
 
45
 
 
46
    >>> bart = user_manager.create_user('bart@example.com')
 
47
    >>> transaction.commit()
 
48
    >>> dump_json('http://localhost:9001/3.0/users')
 
49
    entry 0:
 
50
        created_on: 2005-08-01T07:49:23
 
51
        display_name: Anne Person
 
52
        http_etag: "..."
 
53
        self_link: http://localhost:9001/3.0/users/1
 
54
        user_id: 1
 
55
    entry 1:
 
56
        created_on: 2005-08-01T07:49:23
 
57
        http_etag: "..."
 
58
        self_link: http://localhost:9001/3.0/users/2
 
59
        user_id: 2
 
60
    http_etag: "..."
 
61
    start: 0
 
62
    total_size: 2
 
63
 
 
64
 
 
65
Paginating over user records
 
66
----------------------------
 
67
 
 
68
Instead of returning all the user records at once, it's possible to return
 
69
them in pages by adding the GET parameters ``count`` and ``page`` to the
 
70
request URI.  Page 1 is the first page and ``count`` defines the size of the
 
71
page.
 
72
::
 
73
 
 
74
    >>> dump_json('http://localhost:9001/3.0/users?count=1&page=1')
 
75
    entry 0:
 
76
        created_on: 2005-08-01T07:49:23
 
77
        display_name: Anne Person
 
78
        http_etag: "..."
 
79
        self_link: http://localhost:9001/3.0/users/1
 
80
        user_id: 1
 
81
    http_etag: "..."
 
82
    start: 0
 
83
    total_size: 1
 
84
 
 
85
    >>> dump_json('http://localhost:9001/3.0/users?count=1&page=2')
 
86
    entry 0:
 
87
        created_on: 2005-08-01T07:49:23
 
88
        http_etag: "..."
 
89
        self_link: http://localhost:9001/3.0/users/2
 
90
        user_id: 2
 
91
    http_etag: "..."
 
92
    start: 0
 
93
    total_size: 1
 
94
 
 
95
 
 
96
Creating users
 
97
==============
 
98
 
 
99
New users can be created by POSTing to the users collection.  At a minimum,
 
100
the user's email address must be provided.
 
101
 
 
102
    >>> dump_json('http://localhost:9001/3.0/users', {
 
103
    ...           'email': 'cris@example.com',
 
104
    ...           })
 
105
    content-length: 0
 
106
    date: ...
 
107
    location: http://localhost:9001/3.0/users/3
 
108
    server: ...
 
109
    status: 201
 
110
 
 
111
Cris is now a user known to the system, but he has no display name.
 
112
 
 
113
    >>> user_manager.get_user('cris@example.com')
 
114
    <User "" (3) at ...>
 
115
 
 
116
Cris's user record can also be accessed via the REST API, using her user id.
 
117
Note that because no password was given when the record was created, a random
 
118
one was assigned to her.
 
119
 
 
120
    >>> dump_json('http://localhost:9001/3.0/users/3')
 
121
    created_on: 2005-08-01T07:49:23
 
122
    http_etag: "..."
 
123
    password: {plaintext}...
 
124
    self_link: http://localhost:9001/3.0/users/3
 
125
    user_id: 3
 
126
 
 
127
Because email addresses just have an ``@`` sign in then, there's no confusing
 
128
them with user ids.  Thus, Cris's record can be retrieved via her email
 
129
address.
 
130
 
 
131
    >>> dump_json('http://localhost:9001/3.0/users/cris@example.com')
 
132
    created_on: 2005-08-01T07:49:23
 
133
    http_etag: "..."
 
134
    password: {plaintext}...
 
135
    self_link: http://localhost:9001/3.0/users/3
 
136
    user_id: 3
 
137
 
 
138
 
 
139
Providing a display name
 
140
------------------------
 
141
 
 
142
When a user is added, a display name can be provided.
 
143
 
 
144
    >>> transaction.abort()
 
145
    >>> dump_json('http://localhost:9001/3.0/users', {
 
146
    ...           'email': 'dave@example.com',
 
147
    ...           'display_name': 'Dave Person',
 
148
    ...           })
 
149
    content-length: 0
 
150
    date: ...
 
151
    location: http://localhost:9001/3.0/users/4
 
152
    server: ...
 
153
    status: 201
 
154
 
 
155
Dave's user record includes his display name.
 
156
 
 
157
    >>> dump_json('http://localhost:9001/3.0/users/4')
 
158
    created_on: 2005-08-01T07:49:23
 
159
    display_name: Dave Person
 
160
    http_etag: "..."
 
161
    password: {plaintext}...
 
162
    self_link: http://localhost:9001/3.0/users/4
 
163
    user_id: 4
 
164
 
 
165
 
 
166
Providing passwords
 
167
-------------------
 
168
 
 
169
To avoid getting assigned a random, and irretrievable password (but one which
 
170
can be reset), you can provide a password when the user is created.  By
 
171
default, the password is provided in plain text, and it is hashed by Mailman
 
172
before being stored.
 
173
 
 
174
    >>> transaction.abort()
 
175
    >>> dump_json('http://localhost:9001/3.0/users', {
 
176
    ...           'email': 'elly@example.com',
 
177
    ...           'display_name': 'Elly Person',
 
178
    ...           'password': 'supersekrit',
 
179
    ...           })
 
180
    content-length: 0
 
181
    date: ...
 
182
    location: http://localhost:9001/3.0/users/5
 
183
    server: ...
 
184
    status: 201
 
185
 
 
186
When we view Elly's user record, we can tell that her password has been hashed
 
187
because it has the hash algorithm prefix (i.e. the *{plaintext}* marker).
 
188
 
 
189
    >>> dump_json('http://localhost:9001/3.0/users/5')
 
190
    created_on: 2005-08-01T07:49:23
 
191
    display_name: Elly Person
 
192
    http_etag: "..."
 
193
    password: {plaintext}supersekrit
 
194
    self_link: http://localhost:9001/3.0/users/5
 
195
    user_id: 5
 
196
 
 
197
 
 
198
Updating users
 
199
==============
 
200
 
 
201
Dave's display name can be changed through the REST API.
 
202
 
 
203
    >>> dump_json('http://localhost:9001/3.0/users/4', {
 
204
    ...           'display_name': 'David Person',
 
205
    ...           }, method='PATCH')
 
206
    content-length: 0
 
207
    date: ...
 
208
    server: ...
 
209
    status: 204
 
210
 
 
211
Dave's display name has been updated.
 
212
 
 
213
    >>> dump_json('http://localhost:9001/3.0/users/dave@example.com')
 
214
    created_on: 2005-08-01T07:49:23
 
215
    display_name: David Person
 
216
    http_etag: "..."
 
217
    password: {plaintext}...
 
218
    self_link: http://localhost:9001/3.0/users/4
 
219
    user_id: 4
 
220
 
 
221
Dave can also be assigned a new password by providing in the new cleartext
 
222
password.  Mailman will hash this before it is stored internally.
 
223
 
 
224
    >>> dump_json('http://localhost:9001/3.0/users/4', {
 
225
    ...           'cleartext_password': 'clockwork angels',
 
226
    ...           }, method='PATCH')
 
227
    content-length: 0
 
228
    date: ...
 
229
    server: ...
 
230
    status: 204
 
231
 
 
232
As described above, even though you see *{plaintext}clockwork angels* below,
 
233
it has still been hashed before storage.  The default hashing algorithm for
 
234
the test suite is a plain text hash, but you can see that it works by the
 
235
addition of the algorithm prefix.
 
236
 
 
237
    >>> dump_json('http://localhost:9001/3.0/users/4')
 
238
    created_on: 2005-08-01T07:49:23
 
239
    display_name: David Person
 
240
    http_etag: "..."
 
241
    password: {plaintext}clockwork angels
 
242
    self_link: http://localhost:9001/3.0/users/4
 
243
    user_id: 4
 
244
 
 
245
You can change both the display name and the password by PUTing the full
 
246
resource.
 
247
 
 
248
    >>> dump_json('http://localhost:9001/3.0/users/4', {
 
249
    ...           'display_name': 'David Personhood',
 
250
    ...           'cleartext_password': 'the garden',
 
251
    ...           }, method='PUT')
 
252
    content-length: 0
 
253
    date: ...
 
254
    server: ...
 
255
    status: 204
 
256
 
 
257
Dave's user record has been updated.
 
258
 
 
259
    >>> dump_json('http://localhost:9001/3.0/users/dave@example.com')
 
260
    created_on: 2005-08-01T07:49:23
 
261
    display_name: David Personhood
 
262
    http_etag: "..."
 
263
    password: {plaintext}the garden
 
264
    self_link: http://localhost:9001/3.0/users/4
 
265
    user_id: 4
 
266
 
 
267
 
 
268
Deleting users via the API
 
269
==========================
 
270
 
 
271
Users can also be deleted via the API.
 
272
 
 
273
    >>> dump_json('http://localhost:9001/3.0/users/cris@example.com',
 
274
    ...           method='DELETE')
 
275
    content-length: 0
 
276
    date: ...
 
277
    server: ...
 
278
    status: 204
 
279
 
 
280
 
 
281
User addresses
 
282
==============
 
283
 
 
284
Fred may have any number of email addresses associated with his user account,
 
285
and we can find them all through the API.
 
286
 
 
287
Through some other means, Fred registers a bunch of email addresses and
 
288
associates them with his user account.
 
289
 
 
290
    >>> fred = user_manager.create_user('fred@example.com', 'Fred Person')
 
291
    >>> fred.register('fperson@example.com')
 
292
    <Address: fperson@example.com [not verified] at ...>
 
293
    >>> fred.register('fred.person@example.com')
 
294
    <Address: fred.person@example.com [not verified] at ...>
 
295
    >>> fred.register('Fred.Q.Person@example.com')
 
296
    <Address: Fred.Q.Person@example.com [not verified]
 
297
              key: fred.q.person@example.com at ...>
 
298
    >>> transaction.commit()
 
299
 
 
300
When we access Fred's addresses via the REST API, they are sorted in lexical
 
301
order by original (i.e. case-preserved) email address.
 
302
 
 
303
    >>> dump_json('http://localhost:9001/3.0/users/fred@example.com/addresses')
 
304
    entry 0:
 
305
        email: fred.q.person@example.com
 
306
        http_etag: "..."
 
307
        original_email: Fred.Q.Person@example.com
 
308
        registered_on: 2005-08-01T07:49:23
 
309
        self_link:
 
310
            http://localhost:9001/3.0/addresses/fred.q.person@example.com
 
311
        user: http://localhost:9001/3.0/users/6
 
312
    entry 1:
 
313
        email: fperson@example.com
 
314
        http_etag: "..."
 
315
        original_email: fperson@example.com
 
316
        registered_on: 2005-08-01T07:49:23
 
317
        self_link: http://localhost:9001/3.0/addresses/fperson@example.com
 
318
        user: http://localhost:9001/3.0/users/6
 
319
    entry 2:
 
320
        email: fred.person@example.com
 
321
        http_etag: "..."
 
322
        original_email: fred.person@example.com
 
323
        registered_on: 2005-08-01T07:49:23
 
324
        self_link: http://localhost:9001/3.0/addresses/fred.person@example.com
 
325
        user: http://localhost:9001/3.0/users/6
 
326
    entry 3:
 
327
        display_name: Fred Person
 
328
        email: fred@example.com
 
329
        http_etag: "..."
 
330
        original_email: fred@example.com
 
331
        registered_on: 2005-08-01T07:49:23
 
332
        self_link: http://localhost:9001/3.0/addresses/fred@example.com
 
333
        user: http://localhost:9001/3.0/users/6
 
334
    http_etag: "..."
 
335
    start: 0
 
336
    total_size: 4
 
337
 
 
338
In fact, since these are all associated with Fred's user account, any of the
 
339
addresses can be used to look up Fred's user record.
 
340
::
 
341
 
 
342
    >>> dump_json('http://localhost:9001/3.0/users/fred@example.com')
 
343
    created_on: 2005-08-01T07:49:23
 
344
    display_name: Fred Person
 
345
    http_etag: "..."
 
346
    self_link: http://localhost:9001/3.0/users/6
 
347
    user_id: 6
 
348
 
 
349
    >>> dump_json('http://localhost:9001/3.0/users/fred.person@example.com')
 
350
    created_on: 2005-08-01T07:49:23
 
351
    display_name: Fred Person
 
352
    http_etag: "..."
 
353
    self_link: http://localhost:9001/3.0/users/6
 
354
    user_id: 6
 
355
 
 
356
    >>> dump_json('http://localhost:9001/3.0/users/fperson@example.com')
 
357
    created_on: 2005-08-01T07:49:23
 
358
    display_name: Fred Person
 
359
    http_etag: "..."
 
360
    self_link: http://localhost:9001/3.0/users/6
 
361
    user_id: 6
 
362
 
 
363
    >>> dump_json('http://localhost:9001/3.0/users/Fred.Q.Person@example.com')
 
364
    created_on: 2005-08-01T07:49:23
 
365
    display_name: Fred Person
 
366
    http_etag: "..."
 
367
    self_link: http://localhost:9001/3.0/users/6
 
368
    user_id: 6
 
369
 
 
370
 
 
371
Verifying passwords
 
372
===================
 
373
 
 
374
A user's password is stored internally in hashed form.  Logging in a user is
 
375
the process of verifying a provided clear text password against the hashed
 
376
internal password.
 
377
 
 
378
When Elly was added as a user, she provided a password in the clear.  Now the
 
379
password is hashed and getting her user record returns the hashed password.
 
380
 
 
381
    >>> dump_json('http://localhost:9001/3.0/users/5')
 
382
    created_on: 2005-08-01T07:49:23
 
383
    display_name: Elly Person
 
384
    http_etag: "..."
 
385
    password: {plaintext}supersekrit
 
386
    self_link: http://localhost:9001/3.0/users/5
 
387
    user_id: 5
 
388
 
 
389
Unless the client can run the hashing algorithm on the login text that Elly
 
390
provided, and do its own comparison, the client should let the REST API handle
 
391
password verification.
 
392
 
 
393
This time, Elly successfully logs into Mailman.
 
394
 
 
395
    >>> dump_json('http://localhost:9001/3.0/users/5/login', {
 
396
    ...           'cleartext_password': 'supersekrit',
 
397
    ...           }, method='POST')
 
398
    content-length: 0
 
399
    date: ...
 
400
    server: ...
 
401
    status: 204