password policy enforcement for contacts, #32456
[freeside.git] / FS / FS / ClientAPI / MyAccount / contact.pm
1 package FS::ClientAPI::MyAccount::contact;
2
3 use strict;
4 use FS::Record qw( qsearchs );
5 use FS::cust_main;
6 use FS::cust_contact;
7 use FS::contact;
8
9 sub _custoragent_session_custnum {
10   FS::ClientAPI::MyAccount::_custoragent_session_custnum(@_);
11 }
12
13 sub contact_passwd {
14   my $p = shift;
15   my($context, $session, $custnum) = _custoragent_session_custnum($p);
16   return { 'error' => $session } if $context eq 'error';
17
18   return { 'error' => 'Not logged in as a contact.' }
19     unless $session->{'contactnum'};
20
21   return { 'error' => 'Enter new password' }
22     unless length($p->{'new_password'});
23
24   my $contact = _contact( $session->{'contactnum'}, $custnum )
25     or return { 'error' => "Email not found" };
26
27   my $error = '';
28
29   # use these svc_acct length restrictions??
30   my $conf = new FS::Conf;
31   $error = 'Password too short.'
32     if length($p->{'new_password'}) < ($conf->config('passwordmin') || 6);
33   $error = 'Password too long.'
34     if length($p->{'new_password'}) > ($conf->config('passwordmax') || 8);
35
36   $error ||= $contact->is_password_allowed($p->{'new_password'});
37
38   $error ||= $contact->change_password($p->{'new_password'});
39
40   return { 'error' => $error };
41
42 }
43
44 sub _contact {
45   my( $contactnum, $custnum ) = @_;
46
47   #my $search = { 'custnum' => $custnum };
48   #$search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
49   $custnum =~ /^(\d+)$/ or die "illegal custnum";
50   my $search = " AND cust_contact.selfservice_access IS NOT NULL ".
51                " AND cust_contact.selfservice_access = 'Y' ".
52                " AND ( disabled IS NULL OR disabled = '' )".
53                " AND cust_contact.custnum IS NOT NULL AND cust_contact.custnum = $1";
54 #  $search .= " AND agentnum = ". $session->{'agentnum'} if $context eq 'agent';
55
56   qsearchs( {
57     'table'     => 'contact',
58     #'addl_from' => 'LEFT JOIN cust_main USING ( custnum ) ',
59     'addl_from' => ' LEFT JOIN cust_contact USING ( contactnum ) '.
60                    ' LEFT JOIN cust_main ON ( cust_contact.custnum = cust_main.custnum ) ',
61     'hashref'   => { 'contactnum' => $contactnum, },
62     'extra_sql' => $search, #important
63   } );
64
65 }
66
67 sub list_contacts {
68   my $p = shift;
69
70   my($context, $session, $custnum) = _custoragent_session_custnum($p);
71   return { 'error' => $session } if $context eq 'error';
72
73   my $cust_main = qsearchs('cust_main', { custnum=>$custnum } );
74
75   my @contacts = ( map {
76     my $contact = $_->contact;
77     my @contact_email = $contact->contact_email;
78     { 'contactnum'         => $contact->contactnum,
79       'class'              => $_->contact_classname,
80       'first'              => $contact->first,
81       'last'               => $contact->get('last'),
82       'title'              => $contact->title,
83       'emailaddress'       => join(',', map $_->emailaddress, @contact_email),
84       #TODO: contact phone numbers
85       'comment'            => $_->comment,
86       'selfservice_access' => $_->selfservice_access,
87       #'disabled'           => $contact->disabled,
88     };
89   } $cust_main->cust_contact );
90
91   return { 'error'    => '',
92            'contacts' => \@contacts,
93          };
94 }
95
96 sub edit_contact {
97   my $p = shift;
98
99   my($context, $session, $custnum) = _custoragent_session_custnum($p);
100   return { 'error' => $session } if $context eq 'error';
101
102   #shortcut: logged in as a contact?  that must be the one you want to edit
103   my $contactnum = $p->{contactnum} || $session->{'contactnum'};
104
105   my $contact = _contact( $contactnum, $custnum )
106     or return { 'error' => "Email not found" };
107
108   return { error => "Can't edit a multi-customer contact unless logged in as that contact" }
109     if $contactnum != $session->{'contactnum'}
110     && scalar( $contact->cust_contact ) > 1;
111
112   #my $cust_contact = qsearchs('cust_contact', { contactnum => $contactnum,
113   #                                              custnum    => $custnum,    } )
114   #  or die "guru meditation #4200";
115
116   #TODO: change more fields besides just these
117
118   foreach (qw( first last title emailaddress )) {
119     $contact->$_( $p->{$_} ) if length( $p->{$_} );
120   }
121
122   my $error = $contact->replace;
123
124   return { 'error' => $error, };
125
126 }
127
128 sub delete_contact {
129   my $p = shift;
130
131   my($context, $session, $custnum) = _custoragent_session_custnum($p);
132   return { 'error' => $session } if $context eq 'error';
133
134   return { 'error' => 'Cannot delete the currently-logged in contact.' }
135     if $p->{contactnum} == $session->{contactnum};
136
137   my $cust_contact = qsearchs('cust_contact', { contactnum => $p->{contactnum},
138                                                 custnum    => $custnum,       })
139     or return { 'error' => 'Unknown contactnum' };
140
141   my $contact = $cust_contact->contact;
142
143   my $error = $cust_contact->delete;
144   return { 'error' => $error } if $error;
145
146   unless ( $contact->cust_contact || $contact->prospect_contact ) {
147     $contact->delete;
148   }
149
150   return { 'error' => '', };
151 }
152
153 sub new_contact {
154   my $p = shift;
155
156   my($context, $session, $custnum) = _custoragent_session_custnum($p);
157   return { 'error' => $session } if $context eq 'error';
158
159   #TODO: add phone numbers too
160   #TODO: specify a classnum by name and/or list_contact_classes method
161
162   my $contact = new FS::contact {
163     'custnum' => $custnum,
164     map { $_ => $p->{$_} }
165       qw( first last emailaddress classnum comment selfservice_access )
166   };
167
168   $contact->change_password_fields($p->{_password}) if length($p->{_password});
169
170   my $error = $contact->insert;
171   return { 'error' => $error, };
172 }
173
174 1;