$user->supports("foo") will also return true for User::Hash if there is such a key
[catagits/Catalyst-Plugin-Authentication.git] / notes.pod
1 =pod
2
3 =head1 CORE CONCEPTS
4
5 =item authentication
6
7 Authentication means ensuring the user is who they claim to be. 
8
9 =item authorization
10
11 Once a user's identity is known and authenticated, we can decide what they are
12 authorized to do.
13
14 =head2 Authentication
15
16 The process of authenticating the user is, almost always, asking the user to
17 prove they know a secret that only the real user would know.
18
19 Ways of proving you know a secret are:
20
21 =over 4
22
23 =item cleartext password
24
25 Just tell the secret to the other side to prove you know it.
26
27 =item challange/response
28
29 Prove you know the secret without revealing it, and without the possibility of
30 an attacker gaining access even if they get a copy of the data sent from the
31 client.
32
33 =over 4
34
35 =item 1
36
37 The server sends some random data, unique for each request
38
39 =item 2
40
41 The client combines the random data with the secret, and digests that
42
43 =item 3
44
45 The server combines the data with the password as well, and makes sure the
46 digest is the same as what the user sent.
47
48 =back
49
50 Can possibly be implemented optionally using javascript on the client side if
51 it's enabled.
52
53 =item public key cryptography
54
55 ...
56
57 =back
58
59 =head2 Authorization
60
61 =over 4
62
63 =item Role based classification 
64
65 Each user plays in several roles. Restricted actions can then check that the
66 user belongs to a certain role (for example, for the 'delete' action you might
67 want to ensure that a user is an 'editor').
68
69 Since users are allowed to be in an arbitrary amount of roles the logic is
70 simple and effective.
71
72 =item ACLs
73
74 Cascading on top of roles, and more tightly coupled with catalyst is the
75 possibility of access control lists in two namespaces:
76
77 =over 4
78
79 =item *
80
81 The perl package namespace
82
83 =item *
84
85 The mapped URI space
86
87 =back
88
89 For example, you could say that the controller MyApp::C::AdminPanel requires
90 the role 'admin'.
91
92 This should resemble the way apache can provide access control for
93 C<< <Location ...> >> and C<< <Directory ...> >>.
94
95 =back
96
97 =head1 ASPECTS OF IMPLEMENTATION
98
99 =head2 Authentication
100
101 Authentication needs to take care of two things:
102
103 =over 4
104
105 =item *
106
107 Storage of the user data
108
109 =item *
110
111 Authentication of credentials against storage
112
113 =back
114
115 Ideally the two are decoupled, with the following call chain relationship:
116
117 When application asks the authentication plugin to verify credentials, the
118 credential verification plugin asks storage retrieve the user based on the user
119 ID (the credential plugin knows what part of the credential is a user ID).
120
121 A storage must return an object representing the user, whose API is irrelevant
122 here.
123
124 It is up to the user to synchronize the user info storage with the credential
125 plugin, so that the value retrieved from storage is the value the credential
126 plugin expects.
127
128 For example, the user/password credential plugin will look something like this:
129
130         sub authen_login {
131                 my ( $c, %opts ) = @_;
132                 my $user = $opts{user} || $c->auth_get_user( %opts ); # note that
133                 # credential plugin methods should accept a user object if they are
134                 # provided with one
135
136                 if ( $user->password eq $opts->{password} ) {
137                         ...
138                 }
139         }
140
141 A situation where login/password authentication would have to be changed is if
142 the credential system uses /etc/passwd storage, for example. Since /etc/passwd
143 contains hashes of passwords, we can't compare the password directly. The
144 credential system must know to take care of this.
145
146 The login/password plugin should probably be a bit more lax, making checks on
147 $user to see what the user supports:
148
149         if (my $hash = eval { $user->hashed_password }) {
150                 my $digest = Digest->new( $user->hash_algorithm );
151                 ...
152                 $digest->digest eq $hash; # yes, this is binay, not hex or whatever
153         } elsif (my $crypt = eval { $user->crypted_password }) {
154                 return crypt( $password, $crypt ) eq $crypt;
155         } elsif (my $clear = eval { $user->password }) {
156                 return $passwd eq $clear;
157         } else {
158                 Catalyst::Exception->throw("password storage scheme possibilities exhausted");
159         }
160
161 Or less optimistically
162
163         if ( $user->supports(password => "hashed") ) {
164                 my $hash = $user->hashed_password;
165                 ...
166         }
167
168 C<supports> in the user base class should look like this:
169
170         sub supports {
171                 my ( $self, @path ) = @_;
172
173                 my $cursor = $self->_supports; # this is a nested hash
174
175                 while (@path) {
176                         ref $cursor or return undef;
177                         $cursor = $cursor->{ shift @path };
178                 }
179
180                 return $cursor && !ref($cursor);
181         }
182
183 Which just goes through a nested hash, populated by the implementation class.
184
185 The structure of this hash should be converged by the community, and should
186 look something like this for a plugin that supports everything I can think of:
187
188         {
189                 password => {
190                         hash => 1,
191                         crypt => 1,
192                         clear => 1,
193                 },
194                 key {
195                         gpg => 1,
196                         ssh => 1,
197                         ...
198                 },
199                 secureid => 1,
200                 kerberos => 1, # maybe this needs to be deeper? i dunno
201         },
202
203
204 In the event that the storage backend implies a certain format, the credential
205 verification and storage plugins should ship together, for convenience.
206
207 =head3 Testing for authentication
208
209 Once authenticated the authentication plugin sets the C<user> accessor in the
210 context object to contain the user object.
211
212 =head3 Stores are just models
213
214 To make things more flexible, stores are really just models.
215
216 To make things easy a "default" auth storage can be [automatically] selected
217 for the user for the more common situation of one auth driver per app:
218
219         sub auth_get_user {
220                 my ( $c, %opts ) = @_;
221
222                 ( $opts->{store} || $c->find_auth_store )->auth_get_user( %opts );
223         }
224
225         sub find_auth_store {
226                 my $c = shift;
227
228                 $c->config->{auth}{default_store} || ...;
229         }
230
231 Ofcourse, %opts should not be just delegated blindly in the production
232 versions. Perhaps even L<Params::Validate> is in order.
233
234 =head3 Integration with Catalyst::Plugin::Session
235
236 If a session plugin is loaded (C<< $c->isa("Catalyst::Plugin::Session) >>), it
237 should also store the user ID in C<< $c->session->{user} >> unless
238 C<< $c->config->{authentication}{use_session} >> is a false value (true by
239 default).
240
241 =head2 Authorization
242
243 Authorization uses the same storage backend as authentication - that's why they
244 are decoupled.
245
246 Like the credential verification part, the authorization system simply assumes
247 that the necesseary information is in the object representing the user.
248
249 The API required should be strictly documented, in order to minimize confusion.
250
251 For example, the role authorization plugin may require a C<roles> method:
252
253         use Quantum::Superpositions qw/all any/;
254
255         sub authz_check_roles {
256                 my ( $c, @required_roles ) = @_;
257
258                 all(@required_roles) == $c->user->roles;
259         }
260
261 Which must be arranged by the authentication storage plugin's user class.
262
263 In order to facilitate this all storage plugins, either trivial ones, should
264 use factory methods for generating the user object. This allows the user to
265 override the method, replacing the user class with their own, extended class
266 that provides the necessary glue.
267
268 =head1 DESIRED PLUGINS
269
270 =head2 Credential Verification
271
272 =over 4
273
274 =item login/password
275
276 Requires a C<password> method from the user, checks for equality
277
278 =item unix passwd
279
280 Requires a C<crypted_passwd> method from the user, checks for equality after crypt
281
282 =item challenge response
283
284 pretends to be a login/password compatible plugin, and will fall back to it,
285 unless the client can use javascript to create a digest of the password and a
286 seed instead of sending the actual password.
287
288 =item http auth
289
290 Send 401 status code, read back authentication headers
291
292 =item delegated
293
294 a passive authentication plugin that checks whether the user logged in using
295 the engine's backend (e.g. apache) and provides a handle object representing
296 that user that is not tied to any storage.
297
298 =head2 Storage
299
300 =over 4
301
302 =item DBIC/CDBI
303
304 User table, contains arbitrary fields and relationships.
305
306 This should simply provide the interface for storage plugins based on a table
307 in config.
308
309 =item LDAP
310
311 FIXME I'm not quite sure how LDAP looks from an OO perspective.
312
313 =item htpasswd
314
315 Implies credential verification for the various types of passwords htpasswd
316 supports.
317
318 Can use extra info field for additional interface glue. For example:
319
320         sub roles {
321                 my $self = shift;
322                 split(",", $self->info);
323         }
324
325 =item passwd
326
327 A subset of htpasswd, can use comment field for extended info like htpasswd.
328
329 =item perl hash of objects
330
331 a hash of user object keyed by user ID can prove very convenient for rapid
332 development.
333
334 =back
335
336 =head2 Authorization
337
338 =over 4
339
340 =item roles
341
342 =item ACLs
343
344 A complex interface that layers on roles and automatically checks role
345 membership for certain areas of an application based on a perl data structure.
346
347 =back
348
349 =head1 GUIDELINES
350
351 Avoid new formats - there are enough standard formats out there that we can
352 use. No need for XML, YAML, etc authentication support in the core of these plugins.
353
354 These plugins can be supplemented by ::Simple namespaces that have the extra sugar.
355
356 This is one of the reasons Authentication::Simple sucked so bad - it's file
357 parser was half baked, it was very inefficient, and about 10x as many lines of
358 code it could have been if the user simply had to put a perl hash of arrays in
359 the config.
360
361
362 =head1 TODO
363
364 =over 4
365
366 =item Look into L<Apache::AuthCookie>
367
368  < kgftr|konobi> nothingmuch: Apache::AuthCookie has a really nice way of
369  dealing with multiple types of auth (like roles, groups, etc)
370
371 =back
372
373
374 =head1 API tree
375
376 This tree proposes a list the classes in the authentication/authorization
377 plugins, as well as their methods.
378
379 It's purpose is to help synchronize the authen/authz efforts, and is not to be
380 considered a formal spec.
381
382         * Authentication (umbrella plugin)
383                 - set_authenticated( $user ) # set $c->user, and $c->session->{user_id} = $user->id
384                 - logout # delete $c->user, delete $c->session->{user_id}
385                 - user # currently logged in user, if any
386                 - prepare (extended) # checks session to see if a user should be logged in
387                 * User
388                         - id (the value that can be used to get the user with)
389                         - supports
390                           method name, or hierarchy
391                           The methods listed under a hierarchy mean the methods you can call
392                           if $user->supports( qw/password clear/ ) then you can $user->password safely
393                                 * password
394                                         * clear
395                                                 - password
396                                         * hashed
397                                                 - hashed_password
398                                                 - hash_algorithm
399                                         * crypted
400                                                 - crypted_password
401                                 * roles
402                                         - roles
403                                 * ...
404                 * Store (plugin)
405                   the storage plugin is a thin wrapper around a store model, that
406                   provides a notion of a default store for easy dwimming.
407                   it's two methods are just dump delegations to $c->config->{authentication}{store}
408                         - get_user( $id )
409                         - user_supports( $id )
410                         * DBIC
411                           generates an object, puts it in $c->{authetnciation}{store}
412                           this object inherits the model class but is not really a model,
413                           instead it delegates to the real DBIC model under it
414                         * ... # each store impl has a dual life as a plugin
415                 * Store::...::Model
416                   provides the actual storage model glue. For example, in DBIC get_user
417                   will do a retrieve on the user table.
418                         - get_user( $id ) # $id is username, or other credential a user might type in
419                         - user_supports
420                           like calling ->supports on the user, but applies to all users
421                           if one user supports X but another doesn't, the global check will say they both don't
422                         * Minimal
423                           A nested hash in $c->config->{authentication}{entries}
424                             {
425                               user_id => $user_obj || $hash_ref
426                                 }
427                           Hash ref will auto instantiate into a user obj that provides a default
428                           implementation of supports and some accessors, based on the keys
429                         * DBIC
430                           storage dependent
431                                 - roles # has_many rel
432                                 - password # column
433                                 - crypted_password # column
434                                 - hashed_password # column
435                                 - hash_algorithm # column
436                                 - ... # columns
437                         * UNIX (Unix::PasswdFile, Unix::GroupFile)
438                           provides simple role based authentication
439                           implies the Password credential checker
440                                 - uid
441                                 - name
442                                 - crypted_password
443                                 - gid
444                                 - comment
445                                 - home
446                                 - shell
447                                 - groups/roles # aliases
448                                 - add_group/add_role
449                                   uses group password to temporary add a group/role (newgrp)
450                                   stores the added groups in the session data
451                         * Htpasswd (Apache::Htpasswd)
452                           has optional role based authentication in the info field
453                           ($c->config->{authentication}{htpasswd}{role_delimiter} defaults to ',')
454                           implies the Password credential checker
455                                 - name
456                                 - password # htpasswd -p
457                                 - crypted_password # htpasswd -d
458                                 - hashed_password # htpasswd -s, htpasswd -m
459                                 - hash_algorithm
460                 * Credential (plugin)
461                   all $user args can be either user objects, or user ids that will be queried from
462                   $c->config->{authentication}{store}
463                         * Password
464                                 - login( $user, $password )
465                                   tries hashed password, crypted password, and password based on caps of user obj
466                         * CRAM (requires session)
467                           challange is the session ID
468                           response is hash( challange + ( password | crypted_password | hashed_password ) )
469                           possibly backed by javascript on the client side
470                                 - authenticate_cram( $user, $response )
471                                   hashes $c->sessionid and password variant based on config
472                         * SRP (probably useless, but at least take a look - SSL uses it)
473                           possibly backed by javascript on the client side
474                         * HTTP Auth  (like Catalyst::Plugin::Authentication::CDBI::Basic does)
475                           subclasses Credential::Password, and just calls login automatically from prepare
476                           takes a value to $c->forward to in case the user isn't authenticated, to generate 401
477                         * Apache
478                           logs in a user without a store, by checking apache's authentication data
479                         * Net (chansen)
480                           all of these are Password subclasses
481                                 * FTP
482                                 * SMTP
483                                 * POP3 
484                         * Secure ID
485                         * SSH (chansen)
486                 * Store/Credential hybrid
487                         * LDAP
488                           storage dependent... querying the database is tied with credential checking
489                                 - roles (memberOf)
490                                 - ... (attributes)
491                         * RADIUS (chansen)
492                         * PAM (chansen)
493                         * Service
494                                 * OpenID (chansen)
495                                 * Passport (hah!)
496                                 * TypeKey
497                                 * Sxip
498                         * SAML
499                         * LID
500                         * SMB (chansen)
501                         * Kerberos (chansen)
502                         * SASL (Authen::SASL)
503         * Authorization (plugin)
504                 * Roles
505                   checks a user's membership in roles
506                   uses has_roles( @roles ) if the user class has that method (assumed to be more efficient)
507                   uses roles and compares on it's own otherwise
508                         - check_user_roles( [ $user ], @roles )
509                           returns boolean, whether $c->user (or supplied user) has specified roles
510                         - assert_user_roles( [ $user ] @roles )
511                           throws an exception if $c->user (or supplied user) doesn't have the specified roles
512                 * ACL
513                   allows to add ACL rules to sections of the catalyst app
514                   ACLs apply to perl namespace (MyApp::C::Foo "contains" MyApp::C::Foo::Bar, etc)
515                   or they can apply to URL space (/foo contains /bar)
516                   implies $c->user
517                   takes a value to $c->forward to in case the user isn't authorized (error page)
518                         - restrict_access ( $arg_like_forward_takes, @roles ) # roles to allow
519                         - deny_access ( $arg_like_forward_takes, @roles ) # roles to disallow
520                 * SAML
521         * Login Form (plugin)
522           this is the plugin that will be used for CRAM stuff in javascript
523           http://pajhome.org.uk/crypt/ - bsd license hashes in js
524           http://pajhome.org.uk/crypt/md5/chaplogin.html
525           http://perl-md5-login.sourceforge.net/
526           it's just for convenience
527                 - login_form( [ $store ] )
528                   uses user_supports and isa checks on credentials to create a form
529                   that makes sense for the store provided if no store is provided uses
530                   $c->config->{authenticion}{store}
531                 - process_login
532                   using the same info about stores/credentials, will pick up the data
533                   used to generate a form, and find out what form data to use to login
534                 - prepare
535                   if $c->config->{authentication}{auto_login} is on will call process_login automatically
536