File Coverage

lib/Myco/Core/User.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Myco::Core::User;
2              
3             ##############################################################################
4             # $Id: User.pm,v 1.1.1.1 2006/03/01 21:00:55 sommerb Exp $
5             #
6             # See license and copyright near the end of this file.
7             ##############################################################################
8              
9             =pod
10              
11             =head1 NAME
12              
13             Myco::Core::User - Interface to Myco User Objects
14              
15             =head1 VERSION
16              
17             1.0
18              
19             =cut
20              
21             our $VERSION = 1.0;
22              
23             =pod
24              
25             =head1 SYNOPSIS
26              
27             use Myco::Core::User;
28              
29             # Constructors.
30             my $user = Myco::Core::User->new;
31             # See Myco::Entity for more.
32              
33             # Class Methods.
34              
35             # Instance Methods.
36             my $person = $user->get_person;
37             $user->set_person($person);
38             my $login = $user->login;
39             $login->set_login($login);
40             $user->set_pass($pass);
41             if ($user->chk_pass($pass)) {
42             # Allow access.
43             }
44              
45             $user->save;
46             $user->destroy;
47              
48             =head1 DESCRIPTION
49              
50             This Class provides the basic interface to all Myco user objects. It offers
51             the ability to set and get the login name, and to set and check the password.
52             The password is double-MD5 hash encrypted for security.
53              
54             =cut
55              
56             ##############################################################################
57             # Dependencies
58             ##############################################################################
59             # Module Dependencies and Compiler Pragma
60 1     1   11896 use strict;
  1         2  
  1         47  
61 1     1   5 use warnings;
  1         1  
  1         34  
62 1     1   2508 use Myco::Exceptions;
  0            
  0            
63              
64             ##############################################################################
65             # Programmatic Dependences
66             use Myco;
67             use Digest::MD5 ();
68             use Myco::Core::Person;
69             use Tangram::FlatHash;
70              
71             ##############################################################################
72             # Constants
73             ##############################################################################
74             use constant DEBUG => 0;
75             use constant SECRET => 'YOUR SECRET HERE';
76              
77             ##############################################################################
78             # Class Variables
79             ##############################################################################
80             my $_errors = {};
81              
82             ##############################################################################
83             # Inheritance & Introspection
84             ##############################################################################
85             use base qw(Myco::Entity Myco::Association);
86             my $md = Myco::Entity::Meta->new
87             ( name => __PACKAGE__,
88             access_list => { rw => [qw(admin)] },
89             tangram => { table => 'myco_user', # watch those SQL reserved words!
90             bases => [qw(Myco::Association)] },
91             ui => { displayname => sub { shift->get_displayname },
92             list => { layout => [qw(login)] },
93             view => { layout => [qw(login)] }, },
94             );
95              
96             ##############################################################################
97             # Function and Closure Prototypes
98             #############################################################################
99             ## Use this closure to check that a reference is to a Myco::Core::Person object.
100             my $chk_person = sub {
101             Myco::Exception::DataValidation->throw
102             (error => "'${$_[0]}' is not a Myco::Core::Person object")
103             unless UNIVERSAL::isa(${$_[0]}, 'Myco::Core::Person')
104             };
105             # Use this closure to check that login is at least 4 letters or digits
106             my $chk_login = sub {
107             my $login = $ {$_[0]};
108             Myco::Exception::DataValidation->throw
109             (error => 'Login must be 4 or more characters')
110             if $login !~ /([A-Za-z]|\d){4,}/;
111             };
112             # Use this closure to check that pass is at least 6 letters or digits
113             my $chk_pass = sub {
114             my $pass = $ {$_[0]};
115             Myco::Exception::DataValidation->throw
116             (error => 'Login must be 6 or more characters')
117             if $pass !~ /([A-Za-z]|\d){6,}/;
118             };
119              
120             ##############################################################################
121             # Queries
122             ##############################################################################
123              
124             =head1 QUERIES
125              
126             Myco::Query::Meta::Query objects defining generic and reusable queries for
127             finding Myco::Core::User objects.
128              
129             =head2 default query
130              
131             my $metadata = Myco::Core::User->introspect->get_queries;
132             my $default_query = $metadata->{default};
133             my @results = $default_query->run_query(login => 'doej');
134              
135             Find a user object with a given unique login attribute.
136              
137             =head2 by_person query
138              
139             my $metadata = Myco::Core::User->introspect->get_queries;
140             my $default_query = $metadata->{by_person};
141             my @results = $default_query->run_query(person => $p);
142              
143             Find a user object with a person attribute set to a given Myco::Core::Person
144             object, $p.
145              
146             =cut
147              
148             my $queries = sub {
149             my $md = $_[0]; # Metadata object
150             $md->add_query( name => 'default',
151             remotes => { '$u_' => 'Myco::Core::User', },
152             result_remote => '$u_',
153             params => {
154             login => [ qw($u_ login) ],
155             },
156             filter => { parts => [ { remote => '$u_',
157             attr => 'login',
158             oper => 'eq',
159             param => 'login', },
160             ] },
161             );
162              
163             $md->add_query( name => 'by_person',
164             remotes => { '$u_' => 'Myco::Core::User', },
165             result_remote => '$u_',
166             params => {
167             person => [ qw($u_ person) ],
168             },
169             filter => { parts => [
170             { remote => '$u_',
171             attr => 'person',
172             oper => '==',
173             param => 'person' },
174             ] },
175             );
176             };
177              
178             ##############################################################################
179             # Constructor, etc.
180             ##############################################################################
181              
182             =head1 COMMON ENTITY INTERFACE
183              
184             Constructor, accessors, and other methods -- as inherited from Myco::Entity.
185              
186             =cut
187              
188             ##############################################################################
189             # Attributes & Attribute Accessors / Schema Definition
190             ##############################################################################
191              
192             =head1 ATTRIBUTES
193              
194             Attributes may be initially set during object construction (with C) but
195             otherwise are accessed solely through accessor methods. Typical usage:
196              
197             =over 2
198              
199             =item * Set attribute value
200              
201             $user->set_attribute($value);
202              
203             Check functions (see L) perform data
204             validation. If there is any concern that the set method might be called with
205             invalid data then the call should be wrapped in an C block to catch
206             exceptions that would result.
207              
208             =item * Get attribute value
209              
210             $value = $user->get_attribute;
211              
212             =back
213              
214             Available attributes are listed below, using syntax borrowed from UML class
215             diagrams; for each showing the name, type, default initial value (if any),
216             and, following that, a description.
217              
218             =over 4
219              
220             =cut
221              
222             ##############################################################################
223              
224             =item -person: ref(Myco::Core::Person) = Myco::Core::Person
225              
226             The person object to which this user belongs. Access this object to output
227             name information about a user.
228              
229             =cut
230              
231             $md->add_attribute(name => 'person',
232             type => 'ref',
233             # access_list => { rw => [qw(admin)] },
234             synopsis => 'Person',
235             tangram_options => { check_func => $chk_person,
236             required => 1,
237             class => 'Myco::Core::Person' },
238             );
239              
240              
241             ##############################################################################
242              
243             =item -login: string(128) = undef
244              
245             The userE<39>s login name.
246              
247             =cut
248              
249             $md->add_attribute( name => 'login',
250             type => 'string',
251             # access_list => { rw => [qw(admin)] },
252             synopsis => 'Login Name',
253             ui => { label => 'Login Name' },
254             );
255              
256             ##############################################################################
257              
258             =item -pass: string(32) = undef
259              
260             The userE<39>s login password. Internally, it will be encrypted in a double-MD5
261             hash before being stored in the system.
262              
263             =cut
264              
265             $md->add_attribute( name => 'pass',
266             type => 'string',
267             # access_list => { rw => [qw(admin)] },
268             synopsis => 'Password',
269             ui => { label => 'Password',
270             widget => [ 'password_field' ], },
271             );
272              
273             # These are designed to prevent direct access to the password.
274              
275             sub get_pass {
276             Myco::Exception::MNI->throw
277             (error => 'unknown method/attribute '.__PACKAGE__.'->get_pass called');
278             }
279             sub pass {
280             Myco::Exception::MNI->throw
281             (error => 'unknown method/attribute '.__PACKAGE__.'->pass called');
282             }
283              
284             sub set_pass {
285             my ($self, $pass) = @_;
286             $self->SUPER::set_pass(
287             Digest::MD5::md5_hex(SECRET . Digest::MD5::md5_hex($pass)));
288             Myco::Exception::DataValidation->throw
289             (error => 'Password must be at least 6 characters')
290             if ($pass && length $pass < 6);
291             }
292              
293             =item -roles: hash (string(64} => int) = {}
294              
295             The userE<39>s roles. These are stored in a hash, where the keys are the role
296             names and the values are an integer, usually "1". Mostly, you shouldnE<39>t
297             use the hash to get at the roles, though. See below for the methods specific
298             to Role access.
299              
300             =cut
301              
302             $md->add_attribute( name => 'roles',
303             type => 'flat_hash',
304             # access_list => { rw => [qw(admin)] },
305             synopsis => 'Roles',
306             tangram_options => { table => 'user_roles',
307             key_type => 'string',
308             key_sql => 'VARCHAR(64) NOT NULL',
309             type => 'int',
310             sql => 'INT NOT NULL DEFAULT 1', },
311             );
312             # This is designed to prevent direct access to roles
313             sub roles {
314             Myco::Exception::MNI->throw
315             (error => 'unknown method/attribute '.__PACKAGE__.'->roles called');
316             }
317              
318             =back
319              
320             ##############################################################################
321             # Methods
322             ##############################################################################
323              
324             =head1 ADDED CLASS / INSTANCE METHODS
325              
326             =cut
327              
328             =head2 chk_pass
329              
330             if ($user->chk_pass($pass)) {
331             # Allow access.
332             }
333              
334             Checks the userE<39>s pass word or phrase. Returns true if the pass word or
335             phrase is correct, and false if it is not.
336              
337             =cut
338              
339             sub chk_pass {
340             my ($self, $pass) = @_;
341             # Use Class::Tangram::get() to get the password, because there won't yet
342             # be a user when it's getting checked!
343             my $oldpass = $self->SUPER::get_pass || return;
344             return Digest::MD5::md5_hex(SECRET . Digest::MD5::md5_hex($pass)) eq
345             $oldpass ? 1 : 0;
346             }
347              
348             ##############################################################################
349              
350             =head2 get_roles
351              
352             my @roles = $user->get_roles;
353             my $roles_aref = $user->get_roles;
354              
355             Returns a list (in an array context) or an anonymous array (in a scalar
356             context) of all the roles assigned to the user.
357              
358             =cut
359              
360             sub get_roles {
361             if ($_[0]->SUPER::get_roles) {
362             wantarray ?
363             sort keys %{ $_[0]->SUPER::get_roles } :
364             [ sort keys %{ $_[0]->SUPER::get_roles } ];
365             }
366             }
367              
368             ##############################################################################
369              
370             =head2 add_roles
371              
372             $user->add_roles(@roles);
373              
374             Adds the listed roles to the user. If any role in @roles does not actually
375             exist as a role, then C will throw an exception.
376              
377             =cut
378              
379             sub add_roles {
380             my $self = shift;
381             my $roles = $self->SUPER::get_roles;
382             $self->SUPER::set_roles($roles = {}) unless $roles;
383             }
384              
385             ##############################################################################
386              
387             =head2 del_roles
388              
389             $user->del_roles(@roles);
390              
391             Deletes the listed roles from the user.
392              
393             =cut
394              
395             sub del_roles {
396             my $self = shift;
397             my $roles = $self->SUPER::get_roles;
398             delete @{$roles}{@_};
399             }
400              
401             ##############################################################################
402              
403             =head2 get_roles_hash
404              
405             $user->get_roles_hash;
406              
407             Returns an anonymous hash of all of the roles assigned to the user. The hash
408             keys are the role names, and the values are a simple integer (usually one).
409             This is the internal representation of the roles in the User object, and
410             normally this method will only be used internally.
411              
412             =cut
413              
414             # This absolutely must use the Class::Tangram::get() method. To do otherwise
415             # will likely cause a problem with deep recursion in Myco::Entity.
416             # That's why it's best that this method only be used internally -- no one else
417             # should have permission to use it, really, anyway (except in chk_pass(),
418             # above).
419             sub get_roles_hash { $_[0]->SUPER::get_roles }
420              
421              
422             ##############################################################################
423              
424             =head2 get_displayname
425              
426             $user->get_displayname;
427              
428             Returns the displayname of the person (first and last name) associated with a user.
429              
430             =cut
431              
432             sub get_displayname {
433             my $self = shift;
434             return $self->get_person->displayname;
435             }
436              
437             ##############################################################################
438              
439             =head2 find_user
440              
441             my $u = Myco::Core::User->find_user($person);
442              
443             Finds a user, given a Myco::Core::Person. This is a simple wrapper around the
444             'by_person' query contained in the Myco::Core::User query.
445              
446             =cut
447              
448             sub find_user {
449             my $self = shift;
450             my $p = shift;
451             my ($u) = __PACKAGE__->introspect->get_queries->{by_person}->run
452             (person => $p);
453             return $u;
454             }
455              
456              
457             ##############################################################################
458             # Throw a fatal Exception if $_errors is not empty
459              
460              
461             ##############################################################################
462             # Object Schema Activation and Metadata Finalization
463             ##############################################################################
464             $md->activate_class( queries => $queries );
465              
466             1;
467             __END__