1 package FS::access_user;
4 use vars qw( @ISA $htpasswd_file );
7 use FS::Record qw( qsearch qsearchs dbh );
10 use FS::access_usergroup;
13 @ISA = qw( FS::m2m_Common FS::option_Common FS::Record );
14 #@ISA = qw( FS::m2m_Common FS::option_Common );
16 #kludge htpasswd for now (i hope this bootstraps okay)
17 FS::UID->install_callback( sub {
18 my $conf = new FS::Conf;
19 $htpasswd_file = $conf->base_dir. '/htpasswd';
24 FS::access_user - Object methods for access_user records
30 $record = new FS::access_user \%hash;
31 $record = new FS::access_user { 'column' => 'value' };
33 $error = $record->insert;
35 $error = $new_record->replace($old_record);
37 $error = $record->delete;
39 $error = $record->check;
43 An FS::access_user object represents an internal access user. FS::access_user inherits from
44 FS::Record. The following fields are currently supported:
48 =item usernum - primary key
58 =item disabled - empty or 'Y'
68 Creates a new internal access user. To add the user to the database, see L<"insert">.
70 Note that this stores the hash reference, not a distinct copy of the hash it
71 points to. You can ask the object for a copy with the I<hash> method.
75 # the new method can be inherited from FS::Record, if a table method is defined
77 sub table { 'access_user'; }
79 sub _option_table { 'access_user_pref'; }
80 sub _option_namecol { 'prefname'; }
81 sub _option_valuecol { 'prefvalue'; }
85 Adds this record to the database. If there is an error, returns the error,
86 otherwise returns false.
93 my $error = $self->check;
94 return $error if $error;
96 local $SIG{HUP} = 'IGNORE';
97 local $SIG{INT} = 'IGNORE';
98 local $SIG{QUIT} = 'IGNORE';
99 local $SIG{TERM} = 'IGNORE';
100 local $SIG{TSTP} = 'IGNORE';
101 local $SIG{PIPE} = 'IGNORE';
103 my $oldAutoCommit = $FS::UID::AutoCommit;
104 local $FS::UID::AutoCommit = 0;
107 $error = $self->htpasswd_kludge();
109 $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
113 $error = $self->SUPER::insert(@_);
116 $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
118 #make sure it isn't a dup username? or you could nuke people's passwords
119 #blah. really just should do our own login w/cookies
120 #and auth out of the db in the first place
121 #my $hterror = $self->htpasswd_kludge('-D');
122 #$error .= " - additionally received error cleaning up htpasswd file: $hterror"
126 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
132 sub htpasswd_kludge {
135 #awful kludge to skip setting htpasswd for fs_* users
136 return '' if $self->username =~ /^fs_/;
138 unshift @_, '-c' unless -e $htpasswd_file;
140 system('htpasswd', '-b', @_,
149 return 'htpasswd exited unsucessfully';
155 Delete this record from the database.
162 local $SIG{HUP} = 'IGNORE';
163 local $SIG{INT} = 'IGNORE';
164 local $SIG{QUIT} = 'IGNORE';
165 local $SIG{TERM} = 'IGNORE';
166 local $SIG{TSTP} = 'IGNORE';
167 local $SIG{PIPE} = 'IGNORE';
169 my $oldAutoCommit = $FS::UID::AutoCommit;
170 local $FS::UID::AutoCommit = 0;
174 $self->SUPER::delete(@_)
175 || $self->htpasswd_kludge('-D')
179 $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
182 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
188 =item replace OLD_RECORD
190 Replaces the OLD_RECORD with this one in the database. If there is an error,
191 returns the error, otherwise returns false.
198 my $old = ( ref($_[0]) eq ref($new) )
202 local $SIG{HUP} = 'IGNORE';
203 local $SIG{INT} = 'IGNORE';
204 local $SIG{QUIT} = 'IGNORE';
205 local $SIG{TERM} = 'IGNORE';
206 local $SIG{TSTP} = 'IGNORE';
207 local $SIG{PIPE} = 'IGNORE';
209 my $oldAutoCommit = $FS::UID::AutoCommit;
210 local $FS::UID::AutoCommit = 0;
213 if ( $new->_password ne $old->_password ) {
214 my $error = $new->htpasswd_kludge();
216 $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
221 my $error = $new->SUPER::replace($old, @_);
224 $dbh->rollback or die $dbh->errstr if $oldAutoCommit;
227 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
235 Checks all fields to make sure this is a valid internal access user. If there is
236 an error, returns the error, otherwise returns false. Called by the insert
241 # the check method should currently be supplied - FS::Record contains some
242 # data checking routines
248 $self->ut_numbern('usernum')
249 || $self->ut_alpha_lower('username')
250 || $self->ut_text('_password')
251 || $self->ut_text('last')
252 || $self->ut_text('first')
253 || $self->ut_enum('disabled', [ '', 'Y' ] )
255 return $error if $error;
262 Returns a name string for this user: "Last, First".
268 $self->get('last'). ', '. $self->first;
271 =item access_usergroup
275 sub access_usergroup {
277 qsearch( 'access_usergroup', { 'usernum' => $self->usernum } );
288 #=item access_groupnames
292 #sub access_groupnames {
298 Returns a list of agentnums this user can view (via group membership).
304 my $sth = dbh->prepare(
305 "SELECT DISTINCT agentnum FROM access_usergroup
306 JOIN access_groupagent USING ( groupnum )
308 ) or die dbh->errstr;
309 $sth->execute($self->usernum) or die $sth->errstr;
310 map { $_->[0] } @{ $sth->fetchall_arrayref };
315 Returns a hashref of agentnums this user can view.
321 scalar( { map { $_ => 1 } $self->agentnums } );
324 =item agentnums_sql [ HASHREF | OPTION => VALUE ... ]
326 Returns an sql fragement to select only agentnums this user can view.
328 Options are passed as a hashref or a list. Available options are:
334 The frament will also allow the selection of null agentnums.
338 The fragment will also allow the selection of null agentnums if the current
339 user has the provided access right
343 Optional table name in which agentnum is being checked. Sometimes required to
344 resolve 'column reference "agentnum" is ambiguous' errors.
352 my %opt = ref($_[0]) ? %{$_[0]} : @_;
354 my $agentnum = $opt{'table'} ? $opt{'table'}.'.agentnum' : 'agentnum';
356 my @agentnums = map { "$agentnum = $_" } $self->agentnums;
358 push @agentnums, "$agentnum IS NULL"
360 || ( $opt{'null_right'} && $self->access_right($opt{'null_right'}) );
362 return ' 1 = 0 ' unless scalar(@agentnums);
363 '( '. join( ' OR ', @agentnums ). ' )';
368 Returns true if the user can view the specified agent.
373 my( $self, $agentnum ) = @_;
374 my $sth = dbh->prepare(
375 "SELECT COUNT(*) FROM access_usergroup
376 JOIN access_groupagent USING ( groupnum )
377 WHERE usernum = ? AND agentnum = ?"
378 ) or die dbh->errstr;
379 $sth->execute($self->usernum, $agentnum) or die $sth->errstr;
380 $sth->fetchrow_arrayref->[0];
385 Returns the list of agents this user can view (via group membership), as
394 'hashref' => { disabled=>'' },
395 'extra_sql' => ' AND '. $self->agentnums_sql,
401 Given a right name, returns true if this user has this right (currently via
402 group membership, eventually also via user overrides).
407 my( $self, $rightname ) = @_;
408 my $sth = dbh->prepare("
409 SELECT groupnum FROM access_usergroup
410 LEFT JOIN access_group USING ( groupnum )
411 LEFT JOIN access_right
412 ON ( access_group.groupnum = access_right.rightobjnum )
414 AND righttype = 'FS::access_group'
416 ") or die dbh->errstr;
417 $sth->execute($self->usernum, $rightname) or die $sth->errstr;
418 my $row = $sth->fetchrow_arrayref;
419 $row ? $row->[0] : '';
428 L<FS::Record>, schema.html from the base documentation.