From: Yuval Kogman Date: Sun, 27 Nov 2005 21:14:18 +0000 (+0000) Subject: notes.pod is no longer necessary X-Git-Tag: v0.01^0 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=14e464956300acb7cdfe541649f964d0a0067154;p=catagits%2FCatalyst-Plugin-Authentication.git notes.pod is no longer necessary --- diff --git a/notes.pod b/notes.pod deleted file mode 100644 index 2de9ef9..0000000 --- a/notes.pod +++ /dev/null @@ -1,536 +0,0 @@ -=pod - -=head1 CORE CONCEPTS - -=item authentication - -Authentication means ensuring the user is who they claim to be. - -=item authorization - -Once a user's identity is known and authenticated, we can decide what they are -authorized to do. - -=head2 Authentication - -The process of authenticating the user is, almost always, asking the user to -prove they know a secret that only the real user would know. - -Ways of proving you know a secret are: - -=over 4 - -=item cleartext password - -Just tell the secret to the other side to prove you know it. - -=item challange/response - -Prove you know the secret without revealing it, and without the possibility of -an attacker gaining access even if they get a copy of the data sent from the -client. - -=over 4 - -=item 1 - -The server sends some random data, unique for each request - -=item 2 - -The client combines the random data with the secret, and digests that - -=item 3 - -The server combines the data with the password as well, and makes sure the -digest is the same as what the user sent. - -=back - -Can possibly be implemented optionally using javascript on the client side if -it's enabled. - -=item public key cryptography - -... - -=back - -=head2 Authorization - -=over 4 - -=item Role based classification - -Each user plays in several roles. Restricted actions can then check that the -user belongs to a certain role (for example, for the 'delete' action you might -want to ensure that a user is an 'editor'). - -Since users are allowed to be in an arbitrary amount of roles the logic is -simple and effective. - -=item ACLs - -Cascading on top of roles, and more tightly coupled with catalyst is the -possibility of access control lists in two namespaces: - -=over 4 - -=item * - -The perl package namespace - -=item * - -The mapped URI space - -=back - -For example, you could say that the controller MyApp::C::AdminPanel requires -the role 'admin'. - -This should resemble the way apache can provide access control for -C<< >> and C<< >>. - -=back - -=head1 ASPECTS OF IMPLEMENTATION - -=head2 Authentication - -Authentication needs to take care of two things: - -=over 4 - -=item * - -Storage of the user data - -=item * - -Authentication of credentials against storage - -=back - -Ideally the two are decoupled, with the following call chain relationship: - -When application asks the authentication plugin to verify credentials, the -credential verification plugin asks storage retrieve the user based on the user -ID (the credential plugin knows what part of the credential is a user ID). - -A storage must return an object representing the user, whose API is irrelevant -here. - -It is up to the user to synchronize the user info storage with the credential -plugin, so that the value retrieved from storage is the value the credential -plugin expects. - -For example, the user/password credential plugin will look something like this: - - sub authen_login { - my ( $c, %opts ) = @_; - my $user = $opts{user} || $c->auth_get_user( %opts ); # note that - # credential plugin methods should accept a user object if they are - # provided with one - - if ( $user->password eq $opts->{password} ) { - ... - } - } - -A situation where login/password authentication would have to be changed is if -the credential system uses /etc/passwd storage, for example. Since /etc/passwd -contains hashes of passwords, we can't compare the password directly. The -credential system must know to take care of this. - -The login/password plugin should probably be a bit more lax, making checks on -$user to see what the user supports: - - if (my $hash = eval { $user->hashed_password }) { - my $digest = Digest->new( $user->hash_algorithm ); - ... - $digest->digest eq $hash; # yes, this is binay, not hex or whatever - } elsif (my $crypt = eval { $user->crypted_password }) { - return crypt( $password, $crypt ) eq $crypt; - } elsif (my $clear = eval { $user->password }) { - return $passwd eq $clear; - } else { - Catalyst::Exception->throw("password storage scheme possibilities exhausted"); - } - -Or less optimistically - - if ( $user->supports(password => "hashed") ) { - my $hash = $user->hashed_password; - ... - } - -C in the user base class should look like this: - - sub supports { - my ( $self, @path ) = @_; - - my $cursor = $self->_supports; # this is a nested hash - - while (@path) { - ref $cursor or return undef; - $cursor = $cursor->{ shift @path }; - } - - return $cursor && !ref($cursor); - } - -Which just goes through a nested hash, populated by the implementation class. - -The structure of this hash should be converged by the community, and should -look something like this for a plugin that supports everything I can think of: - - { - password => { - hash => 1, - crypt => 1, - clear => 1, - }, - key { - gpg => 1, - ssh => 1, - ... - }, - secureid => 1, - kerberos => 1, # maybe this needs to be deeper? i dunno - }, - - -In the event that the storage backend implies a certain format, the credential -verification and storage plugins should ship together, for convenience. - -=head3 Testing for authentication - -Once authenticated the authentication plugin sets the C accessor in the -context object to contain the user object. - -=head3 Stores are just models - -To make things more flexible, stores are really just models. - -To make things easy a "default" auth storage can be [automatically] selected -for the user for the more common situation of one auth driver per app: - - sub auth_get_user { - my ( $c, %opts ) = @_; - - ( $opts->{store} || $c->find_auth_store )->auth_get_user( %opts ); - } - - sub find_auth_store { - my $c = shift; - - $c->config->{auth}{default_store} || ...; - } - -Ofcourse, %opts should not be just delegated blindly in the production -versions. Perhaps even L is in order. - -=head3 Integration with Catalyst::Plugin::Session - -If a session plugin is loaded (C<< $c->isa("Catalyst::Plugin::Session) >>), it -should also store the user ID in C<< $c->session->{user} >> unless -C<< $c->config->{authentication}{use_session} >> is a false value (true by -default). - -=head2 Authorization - -Authorization uses the same storage backend as authentication - that's why they -are decoupled. - -Like the credential verification part, the authorization system simply assumes -that the necesseary information is in the object representing the user. - -The API required should be strictly documented, in order to minimize confusion. - -For example, the role authorization plugin may require a C method: - - use Quantum::Superpositions qw/all any/; - - sub authz_check_roles { - my ( $c, @required_roles ) = @_; - - all(@required_roles) == $c->user->roles; - } - -Which must be arranged by the authentication storage plugin's user class. - -In order to facilitate this all storage plugins, either trivial ones, should -use factory methods for generating the user object. This allows the user to -override the method, replacing the user class with their own, extended class -that provides the necessary glue. - -=head1 DESIRED PLUGINS - -=head2 Credential Verification - -=over 4 - -=item login/password - -Requires a C method from the user, checks for equality - -=item unix passwd - -Requires a C method from the user, checks for equality after crypt - -=item challenge response - -pretends to be a login/password compatible plugin, and will fall back to it, -unless the client can use javascript to create a digest of the password and a -seed instead of sending the actual password. - -=item http auth - -Send 401 status code, read back authentication headers - -=item delegated - -a passive authentication plugin that checks whether the user logged in using -the engine's backend (e.g. apache) and provides a handle object representing -that user that is not tied to any storage. - -=head2 Storage - -=over 4 - -=item DBIC/CDBI - -User table, contains arbitrary fields and relationships. - -This should simply provide the interface for storage plugins based on a table -in config. - -=item LDAP - -FIXME I'm not quite sure how LDAP looks from an OO perspective. - -=item htpasswd - -Implies credential verification for the various types of passwords htpasswd -supports. - -Can use extra info field for additional interface glue. For example: - - sub roles { - my $self = shift; - split(",", $self->info); - } - -=item passwd - -A subset of htpasswd, can use comment field for extended info like htpasswd. - -=item perl hash of objects - -a hash of user object keyed by user ID can prove very convenient for rapid -development. - -=back - -=head2 Authorization - -=over 4 - -=item roles - -=item ACLs - -A complex interface that layers on roles and automatically checks role -membership for certain areas of an application based on a perl data structure. - -=back - -=head1 GUIDELINES - -Avoid new formats - there are enough standard formats out there that we can -use. No need for XML, YAML, etc authentication support in the core of these plugins. - -These plugins can be supplemented by ::Simple namespaces that have the extra sugar. - -This is one of the reasons Authentication::Simple sucked so bad - it's file -parser was half baked, it was very inefficient, and about 10x as many lines of -code it could have been if the user simply had to put a perl hash of arrays in -the config. - - -=head1 TODO - -=over 4 - -=item Look into L - - < kgftr|konobi> nothingmuch: Apache::AuthCookie has a really nice way of - dealing with multiple types of auth (like roles, groups, etc) - -=back - - -=head1 API tree - -This tree proposes a list the classes in the authentication/authorization -plugins, as well as their methods. - -It's purpose is to help synchronize the authen/authz efforts, and is not to be -considered a formal spec. - - * Authentication (umbrella plugin) - - set_authenticated( $user ) # set $c->user, and $c->session->{user_id} = $user->id - - logout # delete $c->user, delete $c->session->{user_id} - - user # currently logged in user, if any - - prepare (extended) # checks session to see if a user should be logged in - * User - - id (the value that can be used to get the user with) - - supports - method name, or hierarchy - The methods listed under a hierarchy mean the methods you can call - if $user->supports( qw/password clear/ ) then you can $user->password safely - * password - * clear - - password - * hashed - - hashed_password - - hash_algorithm - * crypted - - crypted_password - * roles - - roles - * ... - * Store (plugin) - the storage plugin is a thin wrapper around a store model, that - provides a notion of a default store for easy dwimming. - it's two methods are just dump delegations to $c->config->{authentication}{store} - - get_user( $id ) - - user_supports( $id ) - * DBIC - generates an object, puts it in $c->{authetnciation}{store} - this object inherits the model class but is not really a model, - instead it delegates to the real DBIC model under it - * ... # each store impl has a dual life as a plugin - * Store::...::Model - provides the actual storage model glue. For example, in DBIC get_user - will do a retrieve on the user table. - - get_user( $id ) # $id is username, or other credential a user might type in - - user_supports - like calling ->supports on the user, but applies to all users - if one user supports X but another doesn't, the global check will say they both don't - * Minimal - A nested hash in $c->config->{authentication}{entries} - { - user_id => $user_obj || $hash_ref - } - Hash ref will auto instantiate into a user obj that provides a default - implementation of supports and some accessors, based on the keys - * DBIC - storage dependent - - roles # has_many rel - - password # column - - crypted_password # column - - hashed_password # column - - hash_algorithm # column - - ... # columns - * UNIX (Unix::PasswdFile, Unix::GroupFile) - provides simple role based authentication - implies the Password credential checker - - uid - - name - - crypted_password - - gid - - comment - - home - - shell - - groups/roles # aliases - - add_group/add_role - uses group password to temporary add a group/role (newgrp) - stores the added groups in the session data - * Htpasswd (Apache::Htpasswd) - has optional role based authentication in the info field - ($c->config->{authentication}{htpasswd}{role_delimiter} defaults to ',') - implies the Password credential checker - - name - - password # htpasswd -p - - crypted_password # htpasswd -d - - hashed_password # htpasswd -s, htpasswd -m - - hash_algorithm - * Credential (plugin) - all $user args can be either user objects, or user ids that will be queried from - $c->config->{authentication}{store} - * Password - - login( $user, $password ) - tries hashed password, crypted password, and password based on caps of user obj - * CRAM (requires session) - challange is the session ID - response is hash( challange + ( password | crypted_password | hashed_password ) ) - possibly backed by javascript on the client side - - authenticate_cram( $user, $response ) - hashes $c->sessionid and password variant based on config - * SRP (probably useless, but at least take a look - SSL uses it) - possibly backed by javascript on the client side - * HTTP Auth (like Catalyst::Plugin::Authentication::CDBI::Basic does) - subclasses Credential::Password, and just calls login automatically from prepare - takes a value to $c->forward to in case the user isn't authenticated, to generate 401 - * Apache - logs in a user without a store, by checking apache's authentication data - * Net (chansen) - all of these are Password subclasses - * FTP - * SMTP - * POP3 - * Secure ID - * SSH (chansen) - * Store/Credential hybrid - * LDAP - storage dependent... querying the database is tied with credential checking - - roles (memberOf) - - ... (attributes) - * RADIUS (chansen) - * PAM (chansen) - * Service - * OpenID (chansen) - * Passport (hah!) - * TypeKey - * Sxip - * SAML - * LID - * SMB (chansen) - * Kerberos (chansen) - * SASL (Authen::SASL) - * Authorization (plugin) - * Roles - checks a user's membership in roles - uses has_roles( @roles ) if the user class has that method (assumed to be more efficient) - uses roles and compares on it's own otherwise - - check_user_roles( [ $user ], @roles ) - returns boolean, whether $c->user (or supplied user) has specified roles - - assert_user_roles( [ $user ] @roles ) - throws an exception if $c->user (or supplied user) doesn't have the specified roles - * ACL - allows to add ACL rules to sections of the catalyst app - ACLs apply to perl namespace (MyApp::C::Foo "contains" MyApp::C::Foo::Bar, etc) - or they can apply to URL space (/foo contains /bar) - implies $c->user - takes a value to $c->forward to in case the user isn't authorized (error page) - - restrict_access ( $arg_like_forward_takes, @roles ) # roles to allow - - deny_access ( $arg_like_forward_takes, @roles ) # roles to disallow - * SAML - * Login Form (plugin) - this is the plugin that will be used for CRAM stuff in javascript - http://pajhome.org.uk/crypt/ - bsd license hashes in js - http://pajhome.org.uk/crypt/md5/chaplogin.html - http://perl-md5-login.sourceforge.net/ - it's just for convenience - - login_form( [ $store ] ) - uses user_supports and isa checks on credentials to create a form - that makes sense for the store provided if no store is provided uses - $c->config->{authenticion}{store} - - process_login - using the same info about stores/credentials, will pick up the data - used to generate a form, and find out what form data to use to login - - prepare - if $c->config->{authentication}{auto_login} is on will call process_login automatically -