[freeside-commits] freeside/FS/FS/part_export acct_google.pm, NONE, 1.1
Mark Wells
mark at wavetail.420.am
Thu Mar 10 18:09:05 PST 2011
Update of /home/cvs/cvsroot/freeside/FS/FS/part_export
In directory wavetail.420.am:/tmp/cvs-serv22701/FS/FS/part_export
Added Files:
acct_google.pm
Log Message:
google account export, #11760
--- NEW FILE: acct_google.pm ---
package FS::part_export::acct_google;
use strict;
use vars qw(%info %SIG $CACHE);
use Tie::IxHash;
use base 'FS::part_export';
tie my %options, 'Tie::IxHash',
'domain' => { label => 'Domain name' },
'username' => { label => 'Admin username' },
'password' => { label => 'Admin password' },
;
# To handle multiple domains, use separate instances of
# the export. We assume that they all have different
# admin logins.
%info = (
'svc' => 'svc_acct',
'desc' => 'Google hosted mail',
'options' => \%options,
'nodomain' => 'Y',
'notes' => <<'END'
Export accounts to the Google Provisioning API. Requires
REST::Google::Apps::Provisioning from CPAN.
END
);
sub rebless { shift; }
sub _export_insert {
my($self, $svc_acct) = (shift, shift);
$svc_acct->finger =~ /^(.*)\s+(\S+)$/;
my ($first, $last) = ($1, $2);
$self->google_request('createUser',
'username' => $svc_acct->username,
'password' => $svc_acct->_password,
'givenName' => $first,
'familyName' => $last,
);
}
sub _export_replace {
my( $self, $new, $old ) = (shift, shift, shift);
# We have to do this in two steps, so do the renameUser last so that
# if it fails partway through the username is still coherent.
if ( $new->_password ne $old->_password
or $new->finger ne $old->finger ) {
$new->finger =~ /^(.*)\s+(\S+)$/;
my ($first, $last) = ($1, $2);
my $error = $self->google_request('updateUser',
'username' => $old->username,
'password' => $new->_password,
'givenName' => $first,
'familyName' => $last,
);
return $error if $error;
}
if ( $new->username ne $old->username ) {
my $error = $self->google_request('renameUser',
'username' => $old->username,
'newname' => $new->username
);
return $error if $error;
}
return;
}
sub _export_delete {
my( $self, $svc_acct ) = (shift, shift);
$self->google_request('deleteUser',
'username' => $svc_acct->username
);
}
sub _export_suspend {
my( $self, $svc_acct ) = (shift, shift);
$self->google_request('updateUser',
'username' => $svc_acct->username,
'suspended' => 'true',
);
}
sub _export_unsuspend {
my( $self, $svc_acct ) = (shift, shift);
$self->google_request('updateUser',
'username' => $svc_acct->username,
'suspended' => 'false',
);
}
my %google_error = (
1000 => 'unknown error',
1001 => 'server busy',
1100 => 'username belongs to a recently deleted account',
1101 => 'user suspended',
1200 => 'domain user limit exceeded',
1201 => 'domain alias limit exceeded',
1202 => 'domain suspended',
1203 => 'feature not available on this domain',
1300 => 'username in use',
1301 => 'user not found',
1302 => 'reserved username',
1400 => 'illegal character in first name',
1401 => 'illegal character in last name',
1402 => 'invalid password',
1403 => 'illegal character in username',
# should be everything we need
);
# Runs the request and returns nothing if it succeeds, or an
# error message.
sub google_request {
my ($self, $method, %opt) = @_;
my $google = $self->google_handle;
return $google->{'error'} if $google->{'error'};
# Throw away the result from this; we don't use it yet.
eval { $google->$method(%opt) };
if ( $@ ) {
return $google_error{ $@->{'error'}->{'errorCode'} } || $@->{'error'};
}
return;
}
# Returns a REST::Google::Apps::Provisioning object which is hooked
# to die {error => stuff} on API errors. The cached auth token
# will be used if possible. If not, try to authenticate. On
# authentication error, the R:G:A:P object will still be returned
# but with $google->{'error'} set to the error message.
sub google_handle {
my $self = shift;
my $class = 'REST::Google::Apps::Provisioning';
eval "use $class";
die "failed to load $class\n" if $@;
$CACHE ||= new Cache::FileCache( {
'namespace' => __PACKAGE__,
'cache_root' => "$FS::UID::cache_dir/cache.$FS::UID::datasrc",
} );
my $google = $class->new( 'domain' => $self->option('domain') );
# REST::Google::Apps::Provisioning lacks error reporting. We deal
# with that by hooking HTTP::Response to throw a useful fatal error
# on failure.
$google->{'lwp'}->add_handler( 'response_done' =>
sub {
my $response = shift;
return if $response->is_success;
my $error = '';
if ( $response->content =~ /^</ ) {
#presume xml
$error = $google->{'xml'}->parse_string($response->content);
}
elsif ( $response->content =~ /=/ ) {
$error = +{ map { if ( /^(\w+)=(.*)$/ ) { lc($1) => $2 } }
split("\n", $response->content)
};
}
else { # have something to say if there is no response...
$error = {'error' => $response->status_line};
}
die $error;
}
);
my $cache_id = $self->exportnum . '_token';
$google->{'token'} = $CACHE->get($cache_id);
if ( !$google->{'token'} ) {
eval {
$google->authenticate(
'username' => $self->option('username'),
'password' => $self->option('password'),
)
};
if ( $@ ) {
# XXX CAPTCHA
$google->{'error'} = $@->{'error'};
$CACHE->remove($cache_id);
return $google;
}
$CACHE->set($cache_id, $google->{'token'}, '1 hour');
}
return $google;
}
1;
More information about the freeside-commits
mailing list