Jifty-Plugin-OAuth-0.04/0000755000076500007650000000000011301114516014333 5ustar sartaksartakJifty-Plugin-OAuth-0.04/Changes0000644000076500007650000000101111301114365015621 0ustar sartaksartak0.04 Wed Nov 18 20:00:49 2009 * Update for test_warnings on new Jifty * /oauth/authorized? should run _after_ app "before" rules * Don't use the consumer's mech to fetch server warnings * Doc fixes * Distribution fixes 0.03 Tue Aug 12 04:39:47 2008 * Fail earlier when an unauthenticated user accesses /oauth/authorize * Many doc fixes 0.02 Tue Apr 08 17:21:48 2008 * separated from the core Jifty distribution 0.01 primordial soup era * life begins in the core Jifty distribution Jifty-Plugin-OAuth-0.04/inc/0000755000076500007650000000000011301114516015104 5ustar sartaksartakJifty-Plugin-OAuth-0.04/inc/Module/0000755000076500007650000000000011301114516016331 5ustar sartaksartakJifty-Plugin-OAuth-0.04/inc/Module/AutoInstall.pm0000644000076500007650000005330611301114415021133 0ustar sartaksartak#line 1 package Module::AutoInstall; use strict; use Cwd (); use ExtUtils::MakeMaker (); use vars qw{$VERSION}; BEGIN { $VERSION = '1.03'; } # special map on pre-defined feature sets my %FeatureMap = ( '' => 'Core Features', # XXX: deprecated '-core' => 'Core Features', ); # various lexical flags my ( @Missing, @Existing, %DisabledTests, $UnderCPAN, $HasCPANPLUS ); my ( $Config, $CheckOnly, $SkipInstall, $AcceptDefault, $TestOnly, $AllDeps ); my ( $PostambleActions, $PostambleUsed ); # See if it's a testing or non-interactive session _accept_default( $ENV{AUTOMATED_TESTING} or ! -t STDIN ); _init(); sub _accept_default { $AcceptDefault = shift; } sub missing_modules { return @Missing; } sub do_install { __PACKAGE__->install( [ $Config ? ( UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) : () ], @Missing, ); } # initialize various flags, and/or perform install sub _init { foreach my $arg ( @ARGV, split( /[\s\t]+/, $ENV{PERL_AUTOINSTALL} || $ENV{PERL_EXTUTILS_AUTOINSTALL} || '' ) ) { if ( $arg =~ /^--config=(.*)$/ ) { $Config = [ split( ',', $1 ) ]; } elsif ( $arg =~ /^--installdeps=(.*)$/ ) { __PACKAGE__->install( $Config, @Missing = split( /,/, $1 ) ); exit 0; } elsif ( $arg =~ /^--default(?:deps)?$/ ) { $AcceptDefault = 1; } elsif ( $arg =~ /^--check(?:deps)?$/ ) { $CheckOnly = 1; } elsif ( $arg =~ /^--skip(?:deps)?$/ ) { $SkipInstall = 1; } elsif ( $arg =~ /^--test(?:only)?$/ ) { $TestOnly = 1; } elsif ( $arg =~ /^--all(?:deps)?$/ ) { $AllDeps = 1; } } } # overrides MakeMaker's prompt() to automatically accept the default choice sub _prompt { goto &ExtUtils::MakeMaker::prompt unless $AcceptDefault; my ( $prompt, $default ) = @_; my $y = ( $default =~ /^[Yy]/ ); print $prompt, ' [', ( $y ? 'Y' : 'y' ), '/', ( $y ? 'n' : 'N' ), '] '; print "$default\n"; return $default; } # the workhorse sub import { my $class = shift; my @args = @_ or return; my $core_all; print "*** $class version " . $class->VERSION . "\n"; print "*** Checking for Perl dependencies...\n"; my $cwd = Cwd::cwd(); $Config = []; my $maxlen = length( ( sort { length($b) <=> length($a) } grep { /^[^\-]/ } map { ref($_) ? ( ( ref($_) eq 'HASH' ) ? keys(%$_) : @{$_} ) : '' } map { +{@args}->{$_} } grep { /^[^\-]/ or /^-core$/i } keys %{ +{@args} } )[0] ); # We want to know if we're under CPAN early to avoid prompting, but # if we aren't going to try and install anything anyway then skip the # check entirely since we don't want to have to load (and configure) # an old CPAN just for a cosmetic message $UnderCPAN = _check_lock(1) unless $SkipInstall; while ( my ( $feature, $modules ) = splice( @args, 0, 2 ) ) { my ( @required, @tests, @skiptests ); my $default = 1; my $conflict = 0; if ( $feature =~ m/^-(\w+)$/ ) { my $option = lc($1); # check for a newer version of myself _update_to( $modules, @_ ) and return if $option eq 'version'; # sets CPAN configuration options $Config = $modules if $option eq 'config'; # promote every features to core status $core_all = ( $modules =~ /^all$/i ) and next if $option eq 'core'; next unless $option eq 'core'; } print "[" . ( $FeatureMap{ lc($feature) } || $feature ) . "]\n"; $modules = [ %{$modules} ] if UNIVERSAL::isa( $modules, 'HASH' ); unshift @$modules, -default => &{ shift(@$modules) } if ( ref( $modules->[0] ) eq 'CODE' ); # XXX: bugward combatability while ( my ( $mod, $arg ) = splice( @$modules, 0, 2 ) ) { if ( $mod =~ m/^-(\w+)$/ ) { my $option = lc($1); $default = $arg if ( $option eq 'default' ); $conflict = $arg if ( $option eq 'conflict' ); @tests = @{$arg} if ( $option eq 'tests' ); @skiptests = @{$arg} if ( $option eq 'skiptests' ); next; } printf( "- %-${maxlen}s ...", $mod ); if ( $arg and $arg =~ /^\D/ ) { unshift @$modules, $arg; $arg = 0; } # XXX: check for conflicts and uninstalls(!) them. my $cur = _load($mod); if (_version_cmp ($cur, $arg) >= 0) { print "loaded. ($cur" . ( $arg ? " >= $arg" : '' ) . ")\n"; push @Existing, $mod => $arg; $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; } else { if (not defined $cur) # indeed missing { print "missing." . ( $arg ? " (would need $arg)" : '' ) . "\n"; } else { # no need to check $arg as _version_cmp ($cur, undef) would satisfy >= above print "too old. ($cur < $arg)\n"; } push @required, $mod => $arg; } } next unless @required; my $mandatory = ( $feature eq '-core' or $core_all ); if ( !$SkipInstall and ( $CheckOnly or ($mandatory and $UnderCPAN) or $AllDeps or _prompt( qq{==> Auto-install the } . ( @required / 2 ) . ( $mandatory ? ' mandatory' : ' optional' ) . qq{ module(s) from CPAN?}, $default ? 'y' : 'n', ) =~ /^[Yy]/ ) ) { push( @Missing, @required ); $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; } elsif ( !$SkipInstall and $default and $mandatory and _prompt( qq{==> The module(s) are mandatory! Really skip?}, 'n', ) =~ /^[Nn]/ ) { push( @Missing, @required ); $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; } else { $DisabledTests{$_} = 1 for map { glob($_) } @tests; } } if ( @Missing and not( $CheckOnly or $UnderCPAN ) ) { require Config; print "*** Dependencies will be installed the next time you type '$Config::Config{make}'.\n"; # make an educated guess of whether we'll need root permission. print " (You may need to do that as the 'root' user.)\n" if eval '$>'; } print "*** $class configuration finished.\n"; chdir $cwd; # import to main:: no strict 'refs'; *{'main::WriteMakefile'} = \&Write if caller(0) eq 'main'; } sub _running_under { my $thing = shift; print <<"END_MESSAGE"; *** Since we're running under ${thing}, I'll just let it take care of the dependency's installation later. END_MESSAGE return 1; } # Check to see if we are currently running under CPAN.pm and/or CPANPLUS; # if we are, then we simply let it taking care of our dependencies sub _check_lock { return unless @Missing or @_; my $cpan_env = $ENV{PERL5_CPAN_IS_RUNNING}; if ($ENV{PERL5_CPANPLUS_IS_RUNNING}) { return _running_under($cpan_env ? 'CPAN' : 'CPANPLUS'); } require CPAN; if ($CPAN::VERSION > '1.89') { if ($cpan_env) { return _running_under('CPAN'); } return; # CPAN.pm new enough, don't need to check further } # last ditch attempt, this -will- configure CPAN, very sorry _load_cpan(1); # force initialize even though it's already loaded # Find the CPAN lock-file my $lock = MM->catfile( $CPAN::Config->{cpan_home}, ".lock" ); return unless -f $lock; # Check the lock local *LOCK; return unless open(LOCK, $lock); if ( ( $^O eq 'MSWin32' ? _under_cpan() : == getppid() ) and ( $CPAN::Config->{prerequisites_policy} || '' ) ne 'ignore' ) { print <<'END_MESSAGE'; *** Since we're running under CPAN, I'll just let it take care of the dependency's installation later. END_MESSAGE return 1; } close LOCK; return; } sub install { my $class = shift; my $i; # used below to strip leading '-' from config keys my @config = ( map { s/^-// if ++$i; $_ } @{ +shift } ); my ( @modules, @installed ); while ( my ( $pkg, $ver ) = splice( @_, 0, 2 ) ) { # grep out those already installed if ( _version_cmp( _load($pkg), $ver ) >= 0 ) { push @installed, $pkg; } else { push @modules, $pkg, $ver; } } return @installed unless @modules; # nothing to do return @installed if _check_lock(); # defer to the CPAN shell print "*** Installing dependencies...\n"; return unless _connected_to('cpan.org'); my %args = @config; my %failed; local *FAILED; if ( $args{do_once} and open( FAILED, '.#autoinstall.failed' ) ) { while () { chomp; $failed{$_}++ } close FAILED; my @newmod; while ( my ( $k, $v ) = splice( @modules, 0, 2 ) ) { push @newmod, ( $k => $v ) unless $failed{$k}; } @modules = @newmod; } if ( _has_cpanplus() and not $ENV{PERL_AUTOINSTALL_PREFER_CPAN} ) { _install_cpanplus( \@modules, \@config ); } else { _install_cpan( \@modules, \@config ); } print "*** $class installation finished.\n"; # see if we have successfully installed them while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { if ( _version_cmp( _load($pkg), $ver ) >= 0 ) { push @installed, $pkg; } elsif ( $args{do_once} and open( FAILED, '>> .#autoinstall.failed' ) ) { print FAILED "$pkg\n"; } } close FAILED if $args{do_once}; return @installed; } sub _install_cpanplus { my @modules = @{ +shift }; my @config = _cpanplus_config( @{ +shift } ); my $installed = 0; require CPANPLUS::Backend; my $cp = CPANPLUS::Backend->new; my $conf = $cp->configure_object; return unless $conf->can('conf') # 0.05x+ with "sudo" support or _can_write($conf->_get_build('base')); # 0.04x # if we're root, set UNINST=1 to avoid trouble unless user asked for it. my $makeflags = $conf->get_conf('makeflags') || ''; if ( UNIVERSAL::isa( $makeflags, 'HASH' ) ) { # 0.03+ uses a hashref here $makeflags->{UNINST} = 1 unless exists $makeflags->{UNINST}; } else { # 0.02 and below uses a scalar $makeflags = join( ' ', split( ' ', $makeflags ), 'UNINST=1' ) if ( $makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' } ); } $conf->set_conf( makeflags => $makeflags ); $conf->set_conf( prereqs => 1 ); while ( my ( $key, $val ) = splice( @config, 0, 2 ) ) { $conf->set_conf( $key, $val ); } my $modtree = $cp->module_tree; while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { print "*** Installing $pkg...\n"; MY::preinstall( $pkg, $ver ) or next if defined &MY::preinstall; my $success; my $obj = $modtree->{$pkg}; if ( $obj and _version_cmp( $obj->{version}, $ver ) >= 0 ) { my $pathname = $pkg; $pathname =~ s/::/\\W/; foreach my $inc ( grep { m/$pathname.pm/i } keys(%INC) ) { delete $INC{$inc}; } my $rv = $cp->install( modules => [ $obj->{module} ] ); if ( $rv and ( $rv->{ $obj->{module} } or $rv->{ok} ) ) { print "*** $pkg successfully installed.\n"; $success = 1; } else { print "*** $pkg installation cancelled.\n"; $success = 0; } $installed += $success; } else { print << "."; *** Could not find a version $ver or above for $pkg; skipping. . } MY::postinstall( $pkg, $ver, $success ) if defined &MY::postinstall; } return $installed; } sub _cpanplus_config { my @config = (); while ( @_ ) { my ($key, $value) = (shift(), shift()); if ( $key eq 'prerequisites_policy' ) { if ( $value eq 'follow' ) { $value = CPANPLUS::Internals::Constants::PREREQ_INSTALL(); } elsif ( $value eq 'ask' ) { $value = CPANPLUS::Internals::Constants::PREREQ_ASK(); } elsif ( $value eq 'ignore' ) { $value = CPANPLUS::Internals::Constants::PREREQ_IGNORE(); } else { die "*** Cannot convert option $key = '$value' to CPANPLUS version.\n"; } } else { die "*** Cannot convert option $key to CPANPLUS version.\n"; } } return @config; } sub _install_cpan { my @modules = @{ +shift }; my @config = @{ +shift }; my $installed = 0; my %args; _load_cpan(); require Config; if (CPAN->VERSION < 1.80) { # no "sudo" support, probe for writableness return unless _can_write( MM->catfile( $CPAN::Config->{cpan_home}, 'sources' ) ) and _can_write( $Config::Config{sitelib} ); } # if we're root, set UNINST=1 to avoid trouble unless user asked for it. my $makeflags = $CPAN::Config->{make_install_arg} || ''; $CPAN::Config->{make_install_arg} = join( ' ', split( ' ', $makeflags ), 'UNINST=1' ) if ( $makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' } ); # don't show start-up info $CPAN::Config->{inhibit_startup_message} = 1; # set additional options while ( my ( $opt, $arg ) = splice( @config, 0, 2 ) ) { ( $args{$opt} = $arg, next ) if $opt =~ /^force$/; # pseudo-option $CPAN::Config->{$opt} = $arg; } local $CPAN::Config->{prerequisites_policy} = 'follow'; while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { MY::preinstall( $pkg, $ver ) or next if defined &MY::preinstall; print "*** Installing $pkg...\n"; my $obj = CPAN::Shell->expand( Module => $pkg ); my $success = 0; if ( $obj and _version_cmp( $obj->cpan_version, $ver ) >= 0 ) { my $pathname = $pkg; $pathname =~ s/::/\\W/; foreach my $inc ( grep { m/$pathname.pm/i } keys(%INC) ) { delete $INC{$inc}; } my $rv = $args{force} ? CPAN::Shell->force( install => $pkg ) : CPAN::Shell->install($pkg); $rv ||= eval { $CPAN::META->instance( 'CPAN::Distribution', $obj->cpan_file, ) ->{install} if $CPAN::META; }; if ( $rv eq 'YES' ) { print "*** $pkg successfully installed.\n"; $success = 1; } else { print "*** $pkg installation failed.\n"; $success = 0; } $installed += $success; } else { print << "."; *** Could not find a version $ver or above for $pkg; skipping. . } MY::postinstall( $pkg, $ver, $success ) if defined &MY::postinstall; } return $installed; } sub _has_cpanplus { return ( $HasCPANPLUS = ( $INC{'CPANPLUS/Config.pm'} or _load('CPANPLUS::Shell::Default') ) ); } # make guesses on whether we're under the CPAN installation directory sub _under_cpan { require Cwd; require File::Spec; my $cwd = File::Spec->canonpath( Cwd::cwd() ); my $cpan = File::Spec->canonpath( $CPAN::Config->{cpan_home} ); return ( index( $cwd, $cpan ) > -1 ); } sub _update_to { my $class = __PACKAGE__; my $ver = shift; return if _version_cmp( _load($class), $ver ) >= 0; # no need to upgrade if ( _prompt( "==> A newer version of $class ($ver) is required. Install?", 'y' ) =~ /^[Nn]/ ) { die "*** Please install $class $ver manually.\n"; } print << "."; *** Trying to fetch it from CPAN... . # install ourselves _load($class) and return $class->import(@_) if $class->install( [], $class, $ver ); print << '.'; exit 1; *** Cannot bootstrap myself. :-( Installation terminated. . } # check if we're connected to some host, using inet_aton sub _connected_to { my $site = shift; return ( ( _load('Socket') and Socket::inet_aton($site) ) or _prompt( qq( *** Your host cannot resolve the domain name '$site', which probably means the Internet connections are unavailable. ==> Should we try to install the required module(s) anyway?), 'n' ) =~ /^[Yy]/ ); } # check if a directory is writable; may create it on demand sub _can_write { my $path = shift; mkdir( $path, 0755 ) unless -e $path; return 1 if -w $path; print << "."; *** You are not allowed to write to the directory '$path'; the installation may fail due to insufficient permissions. . if ( eval '$>' and lc(`sudo -V`) =~ /version/ and _prompt( qq( ==> Should we try to re-execute the autoinstall process with 'sudo'?), ((-t STDIN) ? 'y' : 'n') ) =~ /^[Yy]/ ) { # try to bootstrap ourselves from sudo print << "."; *** Trying to re-execute the autoinstall process with 'sudo'... . my $missing = join( ',', @Missing ); my $config = join( ',', UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) if $Config; return unless system( 'sudo', $^X, $0, "--config=$config", "--installdeps=$missing" ); print << "."; *** The 'sudo' command exited with error! Resuming... . } return _prompt( qq( ==> Should we try to install the required module(s) anyway?), 'n' ) =~ /^[Yy]/; } # load a module and return the version it reports sub _load { my $mod = pop; # class/instance doesn't matter my $file = $mod; $file =~ s|::|/|g; $file .= '.pm'; local $@; return eval { require $file; $mod->VERSION } || ( $@ ? undef: 0 ); } # Load CPAN.pm and it's configuration sub _load_cpan { return if $CPAN::VERSION and $CPAN::Config and not @_; require CPAN; if ( $CPAN::HandleConfig::VERSION ) { # Newer versions of CPAN have a HandleConfig module CPAN::HandleConfig->load; } else { # Older versions had the load method in Config directly CPAN::Config->load; } } # compare two versions, either use Sort::Versions or plain comparison # return values same as <=> sub _version_cmp { my ( $cur, $min ) = @_; return -1 unless defined $cur; # if 0 keep comparing return 1 unless $min; $cur =~ s/\s+$//; # check for version numbers that are not in decimal format if ( ref($cur) or ref($min) or $cur =~ /v|\..*\./ or $min =~ /v|\..*\./ ) { if ( ( $version::VERSION or defined( _load('version') )) and version->can('new') ) { # use version.pm if it is installed. return version->new($cur) <=> version->new($min); } elsif ( $Sort::Versions::VERSION or defined( _load('Sort::Versions') ) ) { # use Sort::Versions as the sorting algorithm for a.b.c versions return Sort::Versions::versioncmp( $cur, $min ); } warn "Cannot reliably compare non-decimal formatted versions.\n" . "Please install version.pm or Sort::Versions.\n"; } # plain comparison local $^W = 0; # shuts off 'not numeric' bugs return $cur <=> $min; } # nothing; this usage is deprecated. sub main::PREREQ_PM { return {}; } sub _make_args { my %args = @_; $args{PREREQ_PM} = { %{ $args{PREREQ_PM} || {} }, @Existing, @Missing } if $UnderCPAN or $TestOnly; if ( $args{EXE_FILES} and -e 'MANIFEST' ) { require ExtUtils::Manifest; my $manifest = ExtUtils::Manifest::maniread('MANIFEST'); $args{EXE_FILES} = [ grep { exists $manifest->{$_} } @{ $args{EXE_FILES} } ]; } $args{test}{TESTS} ||= 't/*.t'; $args{test}{TESTS} = join( ' ', grep { !exists( $DisabledTests{$_} ) } map { glob($_) } split( /\s+/, $args{test}{TESTS} ) ); my $missing = join( ',', @Missing ); my $config = join( ',', UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) if $Config; $PostambleActions = ( ($missing and not $UnderCPAN) ? "\$(PERL) $0 --config=$config --installdeps=$missing" : "\$(NOECHO) \$(NOOP)" ); return %args; } # a wrapper to ExtUtils::MakeMaker::WriteMakefile sub Write { require Carp; Carp::croak "WriteMakefile: Need even number of args" if @_ % 2; if ($CheckOnly) { print << "."; *** Makefile not written in check-only mode. . return; } my %args = _make_args(@_); no strict 'refs'; $PostambleUsed = 0; local *MY::postamble = \&postamble unless defined &MY::postamble; ExtUtils::MakeMaker::WriteMakefile(%args); print << "." unless $PostambleUsed; *** WARNING: Makefile written with customized MY::postamble() without including contents from Module::AutoInstall::postamble() -- auto installation features disabled. Please contact the author. . return 1; } sub postamble { $PostambleUsed = 1; return <<"END_MAKE"; config :: installdeps \t\$(NOECHO) \$(NOOP) checkdeps :: \t\$(PERL) $0 --checkdeps installdeps :: \t$PostambleActions END_MAKE } 1; __END__ #line 1056 Jifty-Plugin-OAuth-0.04/inc/Module/Install/0000755000076500007650000000000011301114516017737 5ustar sartaksartakJifty-Plugin-OAuth-0.04/inc/Module/Install/AutoInstall.pm0000644000076500007650000000227311301114415022536 0ustar sartaksartak#line 1 package Module::Install::AutoInstall; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '0.91'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub AutoInstall { $_[0] } sub run { my $self = shift; $self->auto_install_now(@_); } sub write { my $self = shift; $self->auto_install(@_); } sub auto_install { my $self = shift; return if $self->{done}++; # Flatten array of arrays into a single array my @core = map @$_, map @$_, grep ref, $self->build_requires, $self->requires; my @config = @_; # We'll need Module::AutoInstall $self->include('Module::AutoInstall'); require Module::AutoInstall; Module::AutoInstall->import( (@config ? (-config => \@config) : ()), (@core ? (-core => \@core) : ()), $self->features, ); $self->makemaker_args( Module::AutoInstall::_make_args() ); my $class = ref($self); $self->postamble( "# --- $class section:\n" . Module::AutoInstall::postamble() ); } sub auto_install_now { my $self = shift; $self->auto_install(@_); Module::AutoInstall::do_install(); } 1; Jifty-Plugin-OAuth-0.04/inc/Module/Install/Base.pm0000644000076500007650000000176611301114415021157 0ustar sartaksartak#line 1 package Module::Install::Base; use strict 'vars'; use vars qw{$VERSION}; BEGIN { $VERSION = '0.91'; } # Suspend handler for "redefined" warnings BEGIN { my $w = $SIG{__WARN__}; $SIG{__WARN__} = sub { $w }; } #line 42 sub new { my $class = shift; unless ( defined &{"${class}::call"} ) { *{"${class}::call"} = sub { shift->_top->call(@_) }; } unless ( defined &{"${class}::load"} ) { *{"${class}::load"} = sub { shift->_top->load(@_) }; } bless { @_ }, $class; } #line 61 sub AUTOLOAD { local $@; my $func = eval { shift->_top->autoload } or return; goto &$func; } #line 75 sub _top { $_[0]->{_top}; } #line 90 sub admin { $_[0]->_top->{admin} or Module::Install::Base::FakeAdmin->new; } #line 106 sub is_admin { $_[0]->admin->VERSION; } sub DESTROY {} package Module::Install::Base::FakeAdmin; my $fake; sub new { $fake ||= bless(\@_, $_[0]); } sub AUTOLOAD {} sub DESTROY {} # Restore warning handler BEGIN { $SIG{__WARN__} = $SIG{__WARN__}->(); } 1; #line 154 Jifty-Plugin-OAuth-0.04/inc/Module/Install/Can.pm0000644000076500007650000000333311301114416020777 0ustar sartaksartak#line 1 package Module::Install::Can; use strict; use Config (); use File::Spec (); use ExtUtils::MakeMaker (); use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '0.91'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } # check if we can load some module ### Upgrade this to not have to load the module if possible sub can_use { my ($self, $mod, $ver) = @_; $mod =~ s{::|\\}{/}g; $mod .= '.pm' unless $mod =~ /\.pm$/i; my $pkg = $mod; $pkg =~ s{/}{::}g; $pkg =~ s{\.pm$}{}i; local $@; eval { require $mod; $pkg->VERSION($ver || 0); 1 }; } # check if we can run some command sub can_run { my ($self, $cmd) = @_; my $_cmd = $cmd; return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd)); for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') { next if $dir eq ''; my $abs = File::Spec->catfile($dir, $_[1]); return $abs if (-x $abs or $abs = MM->maybe_command($abs)); } return; } # can we locate a (the) C compiler sub can_cc { my $self = shift; my @chunks = split(/ /, $Config::Config{cc}) or return; # $Config{cc} may contain args; try to find out the program part while (@chunks) { return $self->can_run("@chunks") || (pop(@chunks), next); } return; } # Fix Cygwin bug on maybe_command(); if ( $^O eq 'cygwin' ) { require ExtUtils::MM_Cygwin; require ExtUtils::MM_Win32; if ( ! defined(&ExtUtils::MM_Cygwin::maybe_command) ) { *ExtUtils::MM_Cygwin::maybe_command = sub { my ($self, $file) = @_; if ($file =~ m{^/cygdrive/}i and ExtUtils::MM_Win32->can('maybe_command')) { ExtUtils::MM_Win32->maybe_command($file); } else { ExtUtils::MM_Unix->maybe_command($file); } } } } 1; __END__ #line 156 Jifty-Plugin-OAuth-0.04/inc/Module/Install/Fetch.pm0000644000076500007650000000462711301114416021336 0ustar sartaksartak#line 1 package Module::Install::Fetch; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '0.91'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub get_file { my ($self, %args) = @_; my ($scheme, $host, $path, $file) = $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; if ( $scheme eq 'http' and ! eval { require LWP::Simple; 1 } ) { $args{url} = $args{ftp_url} or (warn("LWP support unavailable!\n"), return); ($scheme, $host, $path, $file) = $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; } $|++; print "Fetching '$file' from $host... "; unless (eval { require Socket; Socket::inet_aton($host) }) { warn "'$host' resolve failed!\n"; return; } return unless $scheme eq 'ftp' or $scheme eq 'http'; require Cwd; my $dir = Cwd::getcwd(); chdir $args{local_dir} or return if exists $args{local_dir}; if (eval { require LWP::Simple; 1 }) { LWP::Simple::mirror($args{url}, $file); } elsif (eval { require Net::FTP; 1 }) { eval { # use Net::FTP to get past firewall my $ftp = Net::FTP->new($host, Passive => 1, Timeout => 600); $ftp->login("anonymous", 'anonymous@example.com'); $ftp->cwd($path); $ftp->binary; $ftp->get($file) or (warn("$!\n"), return); $ftp->quit; } } elsif (my $ftp = $self->can_run('ftp')) { eval { # no Net::FTP, fallback to ftp.exe require FileHandle; my $fh = FileHandle->new; local $SIG{CHLD} = 'IGNORE'; unless ($fh->open("|$ftp -n")) { warn "Couldn't open ftp: $!\n"; chdir $dir; return; } my @dialog = split(/\n/, <<"END_FTP"); open $host user anonymous anonymous\@example.com cd $path binary get $file $file quit END_FTP foreach (@dialog) { $fh->print("$_\n") } $fh->close; } } else { warn "No working 'ftp' program available!\n"; chdir $dir; return; } unless (-f $file) { warn "Fetching failed: $@\n"; chdir $dir; return; } return if exists $args{size} and -s $file != $args{size}; system($args{run}) if exists $args{run}; unlink($file) if $args{remove}; print(((!exists $args{check_for} or -e $args{check_for}) ? "done!" : "failed! ($!)"), "\n"); chdir $dir; return !$?; } 1; Jifty-Plugin-OAuth-0.04/inc/Module/Install/Include.pm0000644000076500007650000000101511301114415021653 0ustar sartaksartak#line 1 package Module::Install::Include; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '0.91'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub include { shift()->admin->include(@_); } sub include_deps { shift()->admin->include_deps(@_); } sub auto_include { shift()->admin->auto_include(@_); } sub auto_include_deps { shift()->admin->auto_include_deps(@_); } sub auto_include_dependent_dists { shift()->admin->auto_include_dependent_dists(@_); } 1; Jifty-Plugin-OAuth-0.04/inc/Module/Install/Makefile.pm0000644000076500007650000001600311301114416022011 0ustar sartaksartak#line 1 package Module::Install::Makefile; use strict 'vars'; use ExtUtils::MakeMaker (); use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '0.91'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } sub Makefile { $_[0] } my %seen = (); sub prompt { shift; # Infinite loop protection my @c = caller(); if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) { die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])"; } # In automated testing, always use defaults if ( $ENV{AUTOMATED_TESTING} and ! $ENV{PERL_MM_USE_DEFAULT} ) { local $ENV{PERL_MM_USE_DEFAULT} = 1; goto &ExtUtils::MakeMaker::prompt; } else { goto &ExtUtils::MakeMaker::prompt; } } sub makemaker_args { my $self = shift; my $args = ( $self->{makemaker_args} ||= {} ); %$args = ( %$args, @_ ); return $args; } # For mm args that take multiple space-seperated args, # append an argument to the current list. sub makemaker_append { my $self = sShift; my $name = shift; my $args = $self->makemaker_args; $args->{name} = defined $args->{$name} ? join( ' ', $args->{name}, @_ ) : join( ' ', @_ ); } sub build_subdirs { my $self = shift; my $subdirs = $self->makemaker_args->{DIR} ||= []; for my $subdir (@_) { push @$subdirs, $subdir; } } sub clean_files { my $self = shift; my $clean = $self->makemaker_args->{clean} ||= {}; %$clean = ( %$clean, FILES => join ' ', grep { length $_ } ($clean->{FILES} || (), @_), ); } sub realclean_files { my $self = shift; my $realclean = $self->makemaker_args->{realclean} ||= {}; %$realclean = ( %$realclean, FILES => join ' ', grep { length $_ } ($realclean->{FILES} || (), @_), ); } sub libs { my $self = shift; my $libs = ref $_[0] ? shift : [ shift ]; $self->makemaker_args( LIBS => $libs ); } sub inc { my $self = shift; $self->makemaker_args( INC => shift ); } my %test_dir = (); sub _wanted_t { /\.t$/ and -f $_ and $test_dir{$File::Find::dir} = 1; } sub tests_recursive { my $self = shift; if ( $self->tests ) { die "tests_recursive will not work if tests are already defined"; } my $dir = shift || 't'; unless ( -d $dir ) { die "tests_recursive dir '$dir' does not exist"; } %test_dir = (); require File::Find; File::Find::find( \&_wanted_t, $dir ); $self->tests( join ' ', map { "$_/*.t" } sort keys %test_dir ); } sub write { my $self = shift; die "&Makefile->write() takes no arguments\n" if @_; # Check the current Perl version my $perl_version = $self->perl_version; if ( $perl_version ) { eval "use $perl_version; 1" or die "ERROR: perl: Version $] is installed, " . "but we need version >= $perl_version"; } # Make sure we have a new enough MakeMaker require ExtUtils::MakeMaker; if ( $perl_version and $self->_cmp($perl_version, '5.006') >= 0 ) { # MakeMaker can complain about module versions that include # an underscore, even though its own version may contain one! # Hence the funny regexp to get rid of it. See RT #35800 # for details. $self->build_requires( 'ExtUtils::MakeMaker' => $ExtUtils::MakeMaker::VERSION =~ /^(\d+\.\d+)/ ); $self->configure_requires( 'ExtUtils::MakeMaker' => $ExtUtils::MakeMaker::VERSION =~ /^(\d+\.\d+)/ ); } else { # Allow legacy-compatibility with 5.005 by depending on the # most recent EU:MM that supported 5.005. $self->build_requires( 'ExtUtils::MakeMaker' => 6.42 ); $self->configure_requires( 'ExtUtils::MakeMaker' => 6.42 ); } # Generate the MakeMaker params my $args = $self->makemaker_args; $args->{DISTNAME} = $self->name; $args->{NAME} = $self->module_name || $self->name; $args->{VERSION} = $self->version; $args->{NAME} =~ s/-/::/g; if ( $self->tests ) { $args->{test} = { TESTS => $self->tests }; } if ( $] >= 5.005 ) { $args->{ABSTRACT} = $self->abstract; $args->{AUTHOR} = $self->author; } if ( eval($ExtUtils::MakeMaker::VERSION) >= 6.10 ) { $args->{NO_META} = 1; } if ( eval($ExtUtils::MakeMaker::VERSION) > 6.17 and $self->sign ) { $args->{SIGN} = 1; } unless ( $self->is_admin ) { delete $args->{SIGN}; } # Merge both kinds of requires into prereq_pm my $prereq = ($args->{PREREQ_PM} ||= {}); %$prereq = ( %$prereq, map { @$_ } map { @$_ } grep $_, ($self->configure_requires, $self->build_requires, $self->requires) ); # Remove any reference to perl, PREREQ_PM doesn't support it delete $args->{PREREQ_PM}->{perl}; # merge both kinds of requires into prereq_pm my $subdirs = ($args->{DIR} ||= []); if ($self->bundles) { foreach my $bundle (@{ $self->bundles }) { my ($file, $dir) = @$bundle; push @$subdirs, $dir if -d $dir; delete $prereq->{$file}; } } if ( my $perl_version = $self->perl_version ) { eval "use $perl_version; 1" or die "ERROR: perl: Version $] is installed, " . "but we need version >= $perl_version"; } $args->{INSTALLDIRS} = $self->installdirs; my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_})} keys %$args; my $user_preop = delete $args{dist}->{PREOP}; if (my $preop = $self->admin->preop($user_preop)) { foreach my $key ( keys %$preop ) { $args{dist}->{$key} = $preop->{$key}; } } my $mm = ExtUtils::MakeMaker::WriteMakefile(%args); $self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile'); } sub fix_up_makefile { my $self = shift; my $makefile_name = shift; my $top_class = ref($self->_top) || ''; my $top_version = $self->_top->VERSION || ''; my $preamble = $self->preamble ? "# Preamble by $top_class $top_version\n" . $self->preamble : ''; my $postamble = "# Postamble by $top_class $top_version\n" . ($self->postamble || ''); local *MAKEFILE; open MAKEFILE, "< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!"; my $makefile = do { local $/; }; close MAKEFILE or die $!; $makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /; $makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g; $makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g; $makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m; $makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m; # Module::Install will never be used to build the Core Perl # Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks # PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist $makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m; #$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m; # Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well. $makefile =~ s/(\"?)-I\$\(PERL_LIB\)\1//g; # XXX - This is currently unused; not sure if it breaks other MM-users # $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg; open MAKEFILE, "> $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!"; print MAKEFILE "$preamble$makefile$postamble" or die $!; close MAKEFILE or die $!; 1; } sub preamble { my ($self, $text) = @_; $self->{preamble} = $text . $self->{preamble} if defined $text; $self->{preamble}; } sub postamble { my ($self, $text) = @_; $self->{postamble} ||= $self->admin->postamble; $self->{postamble} .= $text if defined $text; $self->{postamble} } 1; __END__ #line 394 Jifty-Plugin-OAuth-0.04/inc/Module/Install/Metadata.pm0000644000076500007650000003530411301114415022020 0ustar sartaksartak#line 1 package Module::Install::Metadata; use strict 'vars'; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '0.91'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } my @boolean_keys = qw{ sign }; my @scalar_keys = qw{ name module_name abstract author version distribution_type tests installdirs }; my @tuple_keys = qw{ configure_requires build_requires requires recommends bundles resources }; my @resource_keys = qw{ homepage bugtracker repository }; my @array_keys = qw{ keywords }; sub Meta { shift } sub Meta_BooleanKeys { @boolean_keys } sub Meta_ScalarKeys { @scalar_keys } sub Meta_TupleKeys { @tuple_keys } sub Meta_ResourceKeys { @resource_keys } sub Meta_ArrayKeys { @array_keys } foreach my $key ( @boolean_keys ) { *$key = sub { my $self = shift; if ( defined wantarray and not @_ ) { return $self->{values}->{$key}; } $self->{values}->{$key} = ( @_ ? $_[0] : 1 ); return $self; }; } foreach my $key ( @scalar_keys ) { *$key = sub { my $self = shift; return $self->{values}->{$key} if defined wantarray and !@_; $self->{values}->{$key} = shift; return $self; }; } foreach my $key ( @array_keys ) { *$key = sub { my $self = shift; return $self->{values}->{$key} if defined wantarray and !@_; $self->{values}->{$key} ||= []; push @{$self->{values}->{$key}}, @_; return $self; }; } foreach my $key ( @resource_keys ) { *$key = sub { my $self = shift; unless ( @_ ) { return () unless $self->{values}->{resources}; return map { $_->[1] } grep { $_->[0] eq $key } @{ $self->{values}->{resources} }; } return $self->{values}->{resources}->{$key} unless @_; my $uri = shift or die( "Did not provide a value to $key()" ); $self->resources( $key => $uri ); return 1; }; } foreach my $key ( grep { $_ ne "resources" } @tuple_keys) { *$key = sub { my $self = shift; return $self->{values}->{$key} unless @_; my @added; while ( @_ ) { my $module = shift or last; my $version = shift || 0; push @added, [ $module, $version ]; } push @{ $self->{values}->{$key} }, @added; return map {@$_} @added; }; } # Resource handling my %lc_resource = map { $_ => 1 } qw{ homepage license bugtracker repository }; sub resources { my $self = shift; while ( @_ ) { my $name = shift or last; my $value = shift or next; if ( $name eq lc $name and ! $lc_resource{$name} ) { die("Unsupported reserved lowercase resource '$name'"); } $self->{values}->{resources} ||= []; push @{ $self->{values}->{resources} }, [ $name, $value ]; } $self->{values}->{resources}; } # Aliases for build_requires that will have alternative # meanings in some future version of META.yml. sub test_requires { shift->build_requires(@_) } sub install_requires { shift->build_requires(@_) } # Aliases for installdirs options sub install_as_core { $_[0]->installdirs('perl') } sub install_as_cpan { $_[0]->installdirs('site') } sub install_as_site { $_[0]->installdirs('site') } sub install_as_vendor { $_[0]->installdirs('vendor') } sub dynamic_config { my $self = shift; unless ( @_ ) { warn "You MUST provide an explicit true/false value to dynamic_config\n"; return $self; } $self->{values}->{dynamic_config} = $_[0] ? 1 : 0; return 1; } sub perl_version { my $self = shift; return $self->{values}->{perl_version} unless @_; my $version = shift or die( "Did not provide a value to perl_version()" ); # Normalize the version $version = $self->_perl_version($version); # We don't support the reall old versions unless ( $version >= 5.005 ) { die "Module::Install only supports 5.005 or newer (use ExtUtils::MakeMaker)\n"; } $self->{values}->{perl_version} = $version; } #Stolen from M::B my %license_urls = ( perl => 'http://dev.perl.org/licenses/', apache => 'http://apache.org/licenses/LICENSE-2.0', artistic => 'http://opensource.org/licenses/artistic-license.php', artistic_2 => 'http://opensource.org/licenses/artistic-license-2.0.php', lgpl => 'http://opensource.org/licenses/lgpl-license.php', lgpl2 => 'http://opensource.org/licenses/lgpl-2.1.php', lgpl3 => 'http://opensource.org/licenses/lgpl-3.0.html', bsd => 'http://opensource.org/licenses/bsd-license.php', gpl => 'http://opensource.org/licenses/gpl-license.php', gpl2 => 'http://opensource.org/licenses/gpl-2.0.php', gpl3 => 'http://opensource.org/licenses/gpl-3.0.html', mit => 'http://opensource.org/licenses/mit-license.php', mozilla => 'http://opensource.org/licenses/mozilla1.1.php', open_source => undef, unrestricted => undef, restrictive => undef, unknown => undef, ); sub license { my $self = shift; return $self->{values}->{license} unless @_; my $license = shift or die( 'Did not provide a value to license()' ); $self->{values}->{license} = $license; # Automatically fill in license URLs if ( $license_urls{$license} ) { $self->resources( license => $license_urls{$license} ); } return 1; } sub all_from { my ( $self, $file ) = @_; unless ( defined($file) ) { my $name = $self->name or die( "all_from called with no args without setting name() first" ); $file = join('/', 'lib', split(/-/, $name)) . '.pm'; $file =~ s{.*/}{} unless -e $file; unless ( -e $file ) { die("all_from cannot find $file from $name"); } } unless ( -f $file ) { die("The path '$file' does not exist, or is not a file"); } # Some methods pull from POD instead of code. # If there is a matching .pod, use that instead my $pod = $file; $pod =~ s/\.pm$/.pod/i; $pod = $file unless -e $pod; # Pull the different values $self->name_from($file) unless $self->name; $self->version_from($file) unless $self->version; $self->perl_version_from($file) unless $self->perl_version; $self->author_from($pod) unless $self->author; $self->license_from($pod) unless $self->license; $self->abstract_from($pod) unless $self->abstract; return 1; } sub provides { my $self = shift; my $provides = ( $self->{values}->{provides} ||= {} ); %$provides = (%$provides, @_) if @_; return $provides; } sub auto_provides { my $self = shift; return $self unless $self->is_admin; unless (-e 'MANIFEST') { warn "Cannot deduce auto_provides without a MANIFEST, skipping\n"; return $self; } # Avoid spurious warnings as we are not checking manifest here. local $SIG{__WARN__} = sub {1}; require ExtUtils::Manifest; local *ExtUtils::Manifest::manicheck = sub { return }; require Module::Build; my $build = Module::Build->new( dist_name => $self->name, dist_version => $self->version, license => $self->license, ); $self->provides( %{ $build->find_dist_packages || {} } ); } sub feature { my $self = shift; my $name = shift; my $features = ( $self->{values}->{features} ||= [] ); my $mods; if ( @_ == 1 and ref( $_[0] ) ) { # The user used ->feature like ->features by passing in the second # argument as a reference. Accomodate for that. $mods = $_[0]; } else { $mods = \@_; } my $count = 0; push @$features, ( $name => [ map { ref($_) ? ( ref($_) eq 'HASH' ) ? %$_ : @$_ : $_ } @$mods ] ); return @$features; } sub features { my $self = shift; while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) { $self->feature( $name, @$mods ); } return $self->{values}->{features} ? @{ $self->{values}->{features} } : (); } sub no_index { my $self = shift; my $type = shift; push @{ $self->{values}->{no_index}->{$type} }, @_ if $type; return $self->{values}->{no_index}; } sub read { my $self = shift; $self->include_deps( 'YAML::Tiny', 0 ); require YAML::Tiny; my $data = YAML::Tiny::LoadFile('META.yml'); # Call methods explicitly in case user has already set some values. while ( my ( $key, $value ) = each %$data ) { next unless $self->can($key); if ( ref $value eq 'HASH' ) { while ( my ( $module, $version ) = each %$value ) { $self->can($key)->($self, $module => $version ); } } else { $self->can($key)->($self, $value); } } return $self; } sub write { my $self = shift; return $self unless $self->is_admin; $self->admin->write_meta; return $self; } sub version_from { require ExtUtils::MM_Unix; my ( $self, $file ) = @_; $self->version( ExtUtils::MM_Unix->parse_version($file) ); } sub abstract_from { require ExtUtils::MM_Unix; my ( $self, $file ) = @_; $self->abstract( bless( { DISTNAME => $self->name }, 'ExtUtils::MM_Unix' )->parse_abstract($file) ); } # Add both distribution and module name sub name_from { my ($self, $file) = @_; if ( Module::Install::_read($file) =~ m/ ^ \s* package \s* ([\w:]+) \s* ; /ixms ) { my ($name, $module_name) = ($1, $1); $name =~ s{::}{-}g; $self->name($name); unless ( $self->module_name ) { $self->module_name($module_name); } } else { die("Cannot determine name from $file\n"); } } sub perl_version_from { my $self = shift; if ( Module::Install::_read($_[0]) =~ m/ ^ (?:use|require) \s* v? ([\d_\.]+) \s* ; /ixms ) { my $perl_version = $1; $perl_version =~ s{_}{}g; $self->perl_version($perl_version); } else { warn "Cannot determine perl version info from $_[0]\n"; return; } } sub author_from { my $self = shift; my $content = Module::Install::_read($_[0]); if ($content =~ m/ =head \d \s+ (?:authors?)\b \s* ([^\n]*) | =head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b \s* .*? copyright .*? \d\d\d[\d.]+ \s* (?:\bby\b)? \s* ([^\n]*) /ixms) { my $author = $1 || $2; $author =~ s{E}{<}g; $author =~ s{E}{>}g; $self->author($author); } else { warn "Cannot determine author info from $_[0]\n"; } } sub license_from { my $self = shift; if ( Module::Install::_read($_[0]) =~ m/ ( =head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b .*? ) (=head\\d.*|=cut.*|) \z /ixms ) { my $license_text = $1; my @phrases = ( 'under the same (?:terms|license) as (?:perl|the perl programming language) itself' => 'perl', 1, 'GNU general public license' => 'gpl', 1, 'GNU public license' => 'gpl', 1, 'GNU lesser general public license' => 'lgpl', 1, 'GNU lesser public license' => 'lgpl', 1, 'GNU library general public license' => 'lgpl', 1, 'GNU library public license' => 'lgpl', 1, 'BSD license' => 'bsd', 1, 'Artistic license' => 'artistic', 1, 'GPL' => 'gpl', 1, 'LGPL' => 'lgpl', 1, 'BSD' => 'bsd', 1, 'Artistic' => 'artistic', 1, 'MIT' => 'mit', 1, 'proprietary' => 'proprietary', 0, ); while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) { $pattern =~ s{\s+}{\\s+}g; if ( $license_text =~ /\b$pattern\b/i ) { $self->license($license); return 1; } } } warn "Cannot determine license info from $_[0]\n"; return 'unknown'; } sub _extract_bugtracker { my @links = $_[0] =~ m#L<(\Qhttp://rt.cpan.org/\E[^>]+)>#g; my %links; @links{@links}=(); @links=keys %links; return @links; } sub bugtracker_from { my $self = shift; my $content = Module::Install::_read($_[0]); my @links = _extract_bugtracker($content); unless ( @links ) { warn "Cannot determine bugtracker info from $_[0]\n"; return 0; } if ( @links > 1 ) { warn "Found more than on rt.cpan.org link in $_[0]\n"; return 0; } # Set the bugtracker bugtracker( $links[0] ); return 1; } sub requires_from { my $self = shift; my $content = Module::Install::_readperl($_[0]); my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+([\d\.]+)/mg; while ( @requires ) { my $module = shift @requires; my $version = shift @requires; $self->requires( $module => $version ); } } sub test_requires_from { my $self = shift; my $content = Module::Install::_readperl($_[0]); my @requires = $content =~ m/^use\s+([^\W\d]\w*(?:::\w+)*)\s+([\d\.]+)/mg; while ( @requires ) { my $module = shift @requires; my $version = shift @requires; $self->test_requires( $module => $version ); } } # Convert triple-part versions (eg, 5.6.1 or 5.8.9) to # numbers (eg, 5.006001 or 5.008009). # Also, convert double-part versions (eg, 5.8) sub _perl_version { my $v = $_[-1]; $v =~ s/^([1-9])\.([1-9]\d?\d?)$/sprintf("%d.%03d",$1,$2)/e; $v =~ s/^([1-9])\.([1-9]\d?\d?)\.(0|[1-9]\d?\d?)$/sprintf("%d.%03d%03d",$1,$2,$3 || 0)/e; $v =~ s/(\.\d\d\d)000$/$1/; $v =~ s/_.+$//; if ( ref($v) ) { # Numify $v = $v + 0; } return $v; } ###################################################################### # MYMETA Support sub WriteMyMeta { die "WriteMyMeta has been deprecated"; } sub write_mymeta_yaml { my $self = shift; # We need YAML::Tiny to write the MYMETA.yml file unless ( eval { require YAML::Tiny; 1; } ) { return 1; } # Generate the data my $meta = $self->_write_mymeta_data or return 1; # Save as the MYMETA.yml file print "Writing MYMETA.yml\n"; YAML::Tiny::DumpFile('MYMETA.yml', $meta); } sub write_mymeta_json { my $self = shift; # We need JSON to write the MYMETA.json file unless ( eval { require JSON; 1; } ) { return 1; } # Generate the data my $meta = $self->_write_mymeta_data or return 1; # Save as the MYMETA.yml file print "Writing MYMETA.json\n"; Module::Install::_write( 'MYMETA.json', JSON->new->pretty(1)->canonical->encode($meta), ); } sub _write_mymeta_data { my $self = shift; # If there's no existing META.yml there is nothing we can do return undef unless -f 'META.yml'; # We need Parse::CPAN::Meta to load the file unless ( eval { require Parse::CPAN::Meta; 1; } ) { return undef; } # Merge the perl version into the dependencies my $val = $self->Meta->{values}; my $perl = delete $val->{perl_version}; if ( $perl ) { $val->{requires} ||= []; my $requires = $val->{requires}; # Canonize to three-dot version after Perl 5.6 if ( $perl >= 5.006 ) { $perl =~ s{^(\d+)\.(\d\d\d)(\d*)}{join('.', $1, int($2||0), int($3||0))}e } unshift @$requires, [ perl => $perl ]; } # Load the advisory META.yml file my @yaml = Parse::CPAN::Meta::LoadFile('META.yml'); my $meta = $yaml[0]; # Overwrite the non-configure dependency hashs delete $meta->{requires}; delete $meta->{build_requires}; delete $meta->{recommends}; if ( exists $val->{requires} ) { $meta->{requires} = { map { @$_ } @{ $val->{requires} } }; } if ( exists $val->{build_requires} ) { $meta->{build_requires} = { map { @$_ } @{ $val->{build_requires} } }; } return $meta; } 1; Jifty-Plugin-OAuth-0.04/inc/Module/Install/Win32.pm0000644000076500007650000000340311301114416021176 0ustar sartaksartak#line 1 package Module::Install::Win32; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '0.91'; @ISA = 'Module::Install::Base'; $ISCORE = 1; } # determine if the user needs nmake, and download it if needed sub check_nmake { my $self = shift; $self->load('can_run'); $self->load('get_file'); require Config; return unless ( $^O eq 'MSWin32' and $Config::Config{make} and $Config::Config{make} =~ /^nmake\b/i and ! $self->can_run('nmake') ); print "The required 'nmake' executable not found, fetching it...\n"; require File::Basename; my $rv = $self->get_file( url => 'http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe', ftp_url => 'ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe', local_dir => File::Basename::dirname($^X), size => 51928, run => 'Nmake15.exe /o > nul', check_for => 'Nmake.exe', remove => 1, ); die <<'END_MESSAGE' unless $rv; ------------------------------------------------------------------------------- Since you are using Microsoft Windows, you will need the 'nmake' utility before installation. It's available at: http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe or ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe Please download the file manually, save it to a directory in %PATH% (e.g. C:\WINDOWS\COMMAND\), then launch the MS-DOS command line shell, "cd" to that directory, and run "Nmake15.exe" from there; that will create the 'nmake.exe' file needed by this module. You may then resume the installation process described in README. ------------------------------------------------------------------------------- END_MESSAGE } 1; Jifty-Plugin-OAuth-0.04/inc/Module/Install/WriteAll.pm0000644000076500007650000000222211301114416022015 0ustar sartaksartak#line 1 package Module::Install::WriteAll; use strict; use Module::Install::Base (); use vars qw{$VERSION @ISA $ISCORE}; BEGIN { $VERSION = '0.91';; @ISA = qw{Module::Install::Base}; $ISCORE = 1; } sub WriteAll { my $self = shift; my %args = ( meta => 1, sign => 0, inline => 0, check_nmake => 1, @_, ); $self->sign(1) if $args{sign}; $self->admin->WriteAll(%args) if $self->is_admin; $self->check_nmake if $args{check_nmake}; unless ( $self->makemaker_args->{PL_FILES} ) { $self->makemaker_args( PL_FILES => {} ); } # Until ExtUtils::MakeMaker support MYMETA.yml, make sure # we clean it up properly ourself. $self->realclean_files('MYMETA.yml'); if ( $args{inline} ) { $self->Inline->write; } else { $self->Makefile->write; } # The Makefile write process adds a couple of dependencies, # so write the META.yml files after the Makefile. if ( $args{meta} ) { $self->Meta->write; } # Experimental support for MYMETA if ( $ENV{X_MYMETA} ) { if ( $ENV{X_MYMETA} eq 'JSON' ) { $self->Meta->write_mymeta_json; } else { $self->Meta->write_mymeta_yaml; } } return 1; } 1; Jifty-Plugin-OAuth-0.04/inc/Module/Install.pm0000644000076500007650000002411411301114415020275 0ustar sartaksartak#line 1 package Module::Install; # For any maintainers: # The load order for Module::Install is a bit magic. # It goes something like this... # # IF ( host has Module::Install installed, creating author mode ) { # 1. Makefile.PL calls "use inc::Module::Install" # 2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install # 3. The installed version of inc::Module::Install loads # 4. inc::Module::Install calls "require Module::Install" # 5. The ./inc/ version of Module::Install loads # } ELSE { # 1. Makefile.PL calls "use inc::Module::Install" # 2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install # 3. The ./inc/ version of Module::Install loads # } use 5.005; use strict 'vars'; use vars qw{$VERSION $MAIN}; BEGIN { # All Module::Install core packages now require synchronised versions. # This will be used to ensure we don't accidentally load old or # different versions of modules. # This is not enforced yet, but will be some time in the next few # releases once we can make sure it won't clash with custom # Module::Install extensions. $VERSION = '0.91'; # Storage for the pseudo-singleton $MAIN = undef; *inc::Module::Install::VERSION = *VERSION; @inc::Module::Install::ISA = __PACKAGE__; } # Whether or not inc::Module::Install is actually loaded, the # $INC{inc/Module/Install.pm} is what will still get set as long as # the caller loaded module this in the documented manner. # If not set, the caller may NOT have loaded the bundled version, and thus # they may not have a MI version that works with the Makefile.PL. This would # result in false errors or unexpected behaviour. And we don't want that. my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm'; unless ( $INC{$file} ) { die <<"END_DIE" } Please invoke ${\__PACKAGE__} with: use inc::${\__PACKAGE__}; not: use ${\__PACKAGE__}; END_DIE # If the script that is loading Module::Install is from the future, # then make will detect this and cause it to re-run over and over # again. This is bad. Rather than taking action to touch it (which # is unreliable on some platforms and requires write permissions) # for now we should catch this and refuse to run. if ( -f $0 ) { my $s = (stat($0))[9]; # If the modification time is only slightly in the future, # sleep briefly to remove the problem. my $a = $s - time; if ( $a > 0 and $a < 5 ) { sleep 5 } # Too far in the future, throw an error. my $t = time; if ( $s > $t ) { die <<"END_DIE" } Your installer $0 has a modification time in the future ($s > $t). This is known to create infinite loops in make. Please correct this, then run $0 again. END_DIE } # Build.PL was formerly supported, but no longer is due to excessive # difficulty in implementing every single feature twice. if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" } Module::Install no longer supports Build.PL. It was impossible to maintain duel backends, and has been deprecated. Please remove all Build.PL files and only use the Makefile.PL installer. END_DIE # To save some more typing in Module::Install installers, every... # use inc::Module::Install # ...also acts as an implicit use strict. $^H |= strict::bits(qw(refs subs vars)); use Cwd (); use File::Find (); use File::Path (); use FindBin; sub autoload { my $self = shift; my $who = $self->_caller; my $cwd = Cwd::cwd(); my $sym = "${who}::AUTOLOAD"; $sym->{$cwd} = sub { my $pwd = Cwd::cwd(); if ( my $code = $sym->{$pwd} ) { # Delegate back to parent dirs goto &$code unless $cwd eq $pwd; } $$sym =~ /([^:]+)$/ or die "Cannot autoload $who - $sym"; my $method = $1; if ( uc($method) eq $method ) { # Do nothing return; } elsif ( $method =~ /^_/ and $self->can($method) ) { # Dispatch to the root M:I class return $self->$method(@_); } # Dispatch to the appropriate plugin unshift @_, ( $self, $1 ); goto &{$self->can('call')}; }; } sub import { my $class = shift; my $self = $class->new(@_); my $who = $self->_caller; unless ( -f $self->{file} ) { require "$self->{path}/$self->{dispatch}.pm"; File::Path::mkpath("$self->{prefix}/$self->{author}"); $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self ); $self->{admin}->init; @_ = ($class, _self => $self); goto &{"$self->{name}::import"}; } *{"${who}::AUTOLOAD"} = $self->autoload; $self->preload; # Unregister loader and worker packages so subdirs can use them again delete $INC{"$self->{file}"}; delete $INC{"$self->{path}.pm"}; # Save to the singleton $MAIN = $self; return 1; } sub preload { my $self = shift; unless ( $self->{extensions} ) { $self->load_extensions( "$self->{prefix}/$self->{path}", $self ); } my @exts = @{$self->{extensions}}; unless ( @exts ) { @exts = $self->{admin}->load_all_extensions; } my %seen; foreach my $obj ( @exts ) { while (my ($method, $glob) = each %{ref($obj) . '::'}) { next unless $obj->can($method); next if $method =~ /^_/; next if $method eq uc($method); $seen{$method}++; } } my $who = $self->_caller; foreach my $name ( sort keys %seen ) { *{"${who}::$name"} = sub { ${"${who}::AUTOLOAD"} = "${who}::$name"; goto &{"${who}::AUTOLOAD"}; }; } } sub new { my ($class, %args) = @_; # ignore the prefix on extension modules built from top level. my $base_path = Cwd::abs_path($FindBin::Bin); unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) { delete $args{prefix}; } return $args{_self} if $args{_self}; $args{dispatch} ||= 'Admin'; $args{prefix} ||= 'inc'; $args{author} ||= ($^O eq 'VMS' ? '_author' : '.author'); $args{bundle} ||= 'inc/BUNDLES'; $args{base} ||= $base_path; $class =~ s/^\Q$args{prefix}\E:://; $args{name} ||= $class; $args{version} ||= $class->VERSION; unless ( $args{path} ) { $args{path} = $args{name}; $args{path} =~ s!::!/!g; } $args{file} ||= "$args{base}/$args{prefix}/$args{path}.pm"; $args{wrote} = 0; bless( \%args, $class ); } sub call { my ($self, $method) = @_; my $obj = $self->load($method) or return; splice(@_, 0, 2, $obj); goto &{$obj->can($method)}; } sub load { my ($self, $method) = @_; $self->load_extensions( "$self->{prefix}/$self->{path}", $self ) unless $self->{extensions}; foreach my $obj (@{$self->{extensions}}) { return $obj if $obj->can($method); } my $admin = $self->{admin} or die <<"END_DIE"; The '$method' method does not exist in the '$self->{prefix}' path! Please remove the '$self->{prefix}' directory and run $0 again to load it. END_DIE my $obj = $admin->load($method, 1); push @{$self->{extensions}}, $obj; $obj; } sub load_extensions { my ($self, $path, $top) = @_; unless ( grep { ! ref $_ and lc $_ eq lc $self->{prefix} } @INC ) { unshift @INC, $self->{prefix}; } foreach my $rv ( $self->find_extensions($path) ) { my ($file, $pkg) = @{$rv}; next if $self->{pathnames}{$pkg}; local $@; my $new = eval { require $file; $pkg->can('new') }; unless ( $new ) { warn $@ if $@; next; } $self->{pathnames}{$pkg} = delete $INC{$file}; push @{$self->{extensions}}, &{$new}($pkg, _top => $top ); } $self->{extensions} ||= []; } sub find_extensions { my ($self, $path) = @_; my @found; File::Find::find( sub { my $file = $File::Find::name; return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is; my $subpath = $1; return if lc($subpath) eq lc($self->{dispatch}); $file = "$self->{path}/$subpath.pm"; my $pkg = "$self->{name}::$subpath"; $pkg =~ s!/!::!g; # If we have a mixed-case package name, assume case has been preserved # correctly. Otherwise, root through the file to locate the case-preserved # version of the package name. if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) { my $content = Module::Install::_read($subpath . '.pm'); my $in_pod = 0; foreach ( split //, $content ) { $in_pod = 1 if /^=\w/; $in_pod = 0 if /^=cut/; next if ($in_pod || /^=cut/); # skip pod text next if /^\s*#/; # and comments if ( m/^\s*package\s+($pkg)\s*;/i ) { $pkg = $1; last; } } } push @found, [ $file, $pkg ]; }, $path ) if -d $path; @found; } ##################################################################### # Common Utility Functions sub _caller { my $depth = 0; my $call = caller($depth); while ( $call eq __PACKAGE__ ) { $depth++; $call = caller($depth); } return $call; } sub _read { local *FH; if ( $] >= 5.006 ) { open( FH, '<', $_[0] ) or die "open($_[0]): $!"; } else { open( FH, "< $_[0]" ) or die "open($_[0]): $!"; } my $string = do { local $/; }; close FH or die "close($_[0]): $!"; return $string; } sub _readperl { my $string = Module::Install::_read($_[0]); $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; $string =~ s/(\n)\n*__(?:DATA|END)__\b.*\z/$1/s; $string =~ s/\n\n=\w+.+?\n\n=cut\b.+?\n+/\n\n/sg; return $string; } sub _readpod { my $string = Module::Install::_read($_[0]); $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg; return $string if $_[0] =~ /\.pod\z/; $string =~ s/(^|\n=cut\b.+?\n+)[^=\s].+?\n(\n=\w+|\z)/$1$2/sg; $string =~ s/\n*=pod\b[^\n]*\n+/\n\n/sg; $string =~ s/\n*=cut\b[^\n]*\n+/\n\n/sg; $string =~ s/^\n+//s; return $string; } sub _write { local *FH; if ( $] >= 5.006 ) { open( FH, '>', $_[0] ) or die "open($_[0]): $!"; } else { open( FH, "> $_[0]" ) or die "open($_[0]): $!"; } foreach ( 1 .. $#_ ) { print FH $_[$_] or die "print($_[0]): $!"; } close FH or die "close($_[0]): $!"; } # _version is for processing module versions (eg, 1.03_05) not # Perl versions (eg, 5.8.1). sub _version ($) { my $s = shift || 0; my $d =()= $s =~ /(\.)/g; if ( $d >= 2 ) { # Normalise multipart versions $s =~ s/(\.)(\d{1,3})/sprintf("$1%03d",$2)/eg; } $s =~ s/^(\d+)\.?//; my $l = $1 || 0; my @v = map { $_ . '0' x (3 - length $_) } $s =~ /(\d{1,3})\D?/g; $l = $l . '.' . join '', @v if @v; return $l + 0; } sub _cmp ($$) { _version($_[0]) <=> _version($_[1]); } # Cloned from Params::Util::_CLASS sub _CLASS ($) { ( defined $_[0] and ! ref $_[0] and $_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s ) ? $_[0] : undef; } 1; # Copyright 2008 - 2009 Adam Kennedy. Jifty-Plugin-OAuth-0.04/lib/0000755000076500007650000000000011301114516015101 5ustar sartaksartakJifty-Plugin-OAuth-0.04/lib/Jifty/0000755000076500007650000000000011301114516016166 5ustar sartaksartakJifty-Plugin-OAuth-0.04/lib/Jifty/Plugin/0000755000076500007650000000000011301114516017424 5ustar sartaksartakJifty-Plugin-OAuth-0.04/lib/Jifty/Plugin/OAuth/0000755000076500007650000000000011301114516020444 5ustar sartaksartakJifty-Plugin-OAuth-0.04/lib/Jifty/Plugin/OAuth/Action/0000755000076500007650000000000011301114516021661 5ustar sartaksartakJifty-Plugin-OAuth-0.04/lib/Jifty/Plugin/OAuth/Action/AuthorizeRequestToken.pm0000644000076500007650000000713211174267043026562 0ustar sartaksartakpackage Jifty::Plugin::OAuth::Action::AuthorizeRequestToken; use warnings; use strict; use base qw/Jifty::Action/; =head1 NAME Jifty::Plugin::OAuth::Action::AuthorizeRequestToken =cut use Jifty::Param::Schema; use Jifty::Action schema { param 'token', render as 'text', max_length is 30, hints are 'The site you just came from should have provided it', ajax validates; param 'authorize', render as 'select', valid_values are qw(allow deny); param 'callback', render as 'hidden'; param 'use_limit', label is 'Use limit', hints are 'How long should the site have access?', render as 'select', default is '1 hour', valid_values are ( '5 minutes', '1 hour', '1 day', '1 week', ); param 'can_write', label is 'Write access?', hints are 'Should the site be allowed to update your data? (unchecking restricts to read-only)', render as 'checkbox', default is 0; }; =head2 validate_token Make sure we have such a token, and that it is not already authorized =cut sub validate_token { my $self = shift; my $token = shift; my $request_token = Jifty::Plugin::OAuth::Model::RequestToken->new(current_user => Jifty::CurrentUser->superuser); $request_token->load_by_cols( token => $token, authorized => 0, ); return $self->validation_error(token => "I don't know of that request token.") unless $request_token->id; if ($request_token->valid_until < Jifty::DateTime->now(time_zone => 'GMT')) { $request_token->delete(); return $self->validation_error(token => "This request token has expired."); } return $self->validation_ok('token'); } =head2 take_action Actually authorize or deny this request token =cut sub take_action { my $self = shift; my $token = Jifty::Plugin::OAuth::Model::RequestToken->new(current_user => Jifty::CurrentUser->superuser); $token->load_by_cols( token => $self->argument_value('token'), ); $self->result->content(token_obj => $token); $self->result->content(token => $token->token); for (qw/callback use_limit can_write/) { $self->result->content($_ => $self->argument_value($_)); } if ($self->argument_value('authorize') eq 'allow') { $token->set_authorized(1); $token->set_access_token_restrictions({ can_write => $self->argument_value('can_write'), use_limit => $self->inflate_use_limit, }); my $right = $self->argument_value('can_write') ? "read and write" : "read"; $self->result->message("Allowing " . $token->consumer->name . " to $right your data for ". $self->argument_value('use_limit') ."."); } else { $token->delete; $self->result->message("Denying " . $token->consumer->name . " the right to access your data."); } return 1; } =head2 inflate_use_limit -> DateTime Takes the use_limit argument and inflates it to a DateTime object representing when the access token will expire. It expects the input to be of the form "number_of_periods period_length", so "5 minutes", "1 hour", etc. =cut sub inflate_use_limit { my $self = shift; my $use_limit = $self->argument_value('use_limit'); my ($periods, $length) = $use_limit =~ m{^(\d+)\s+(\w+)$} or die "AuthorizeRequestToken->inflate_use_limit failed to parse input $use_limit"; # DateTime::Duration accepts only plurals $length .= 's' if $periods == 1; return DateTime->now->add($length => $periods); } 1; Jifty-Plugin-OAuth-0.04/lib/Jifty/Plugin/OAuth/Dispatcher.pm0000644000076500007650000003330611204326742023105 0ustar sartaksartakpackage Jifty::Plugin::OAuth::Dispatcher; use warnings; use strict; use Jifty::Dispatcher -base; use Net::OAuth::RequestTokenRequest; use Net::OAuth::AccessTokenRequest; use Net::OAuth::ProtectedResourceRequest; use URI::Escape 'uri_unescape'; # Let the app do auth, etc, before we let these rules run after app, before '/oauth/authorized' => redirect '/oauth/authorize'; after app, before GET '/oauth/authorize' => \&authorize; on POST '/oauth/authorize' => \&authorize_post; on GET '/oauth/access_token' => \&invalid_method; on POST '/oauth/access_token' => \&access_token; on GET '/oauth/request_token' => \&invalid_method; on POST '/oauth/request_token' => \&request_token; before '*' => \&try_oauth; =head2 abortmsg CODE, MSG Helper function to abort with a debug message. Maybe should be factored into the C procedure? =cut sub abortmsg { my ($code, $msg) = @_; if ($code) { Jifty->log->debug("$code for ".Jifty->web->request->path.": $msg") if defined($msg); abort($code); } elsif (defined $msg) { Jifty->log->debug("OAuth denied for ".Jifty->web->request->path.": $msg"); } } =head2 request_token The consumer wants a request token =cut sub request_token { my @params = qw/consumer_key signature_method signature timestamp nonce version/; set no_abort => 0; my %oauth_params = get_parameters(@params); my $consumer = get_consumer($oauth_params{consumer_key}); my $signature_key = get_signature_key($oauth_params{signature_method}, $consumer); my ($ok, $msg) = $consumer->is_valid_request(@oauth_params{qw/timestamp nonce/}); abortmsg(401, $msg) if !$ok; # Net::OAuth::Request will die hard if it doesn't get everything it wants my $request = eval { Net::OAuth::RequestTokenRequest->new( request_url => Jifty->web->url(path => '/oauth/request_token'), request_method => Jifty->handler->apache->method(), consumer_secret => $consumer->secret, signature_key => $signature_key, map { $_ => $oauth_params{$_} } @params ) }; abortmsg(400, "Unable to create RequestTokenRequest: $@") if $@ || !defined($request); # make sure the signature matches the rest of what the consumer gave us abortmsg(401, "Invalid signature (type: $oauth_params{signature_method}).") unless $request->verify; # ok, everything checks out. send them back a request token # at this point, the only things that could go wrong are: # 1) we've already seen this nonce and timestamp. possibly a replay attack, # so we abort # 2) we tried a bunch of times to create a unique token but failed. abort # because we don't have any other option my $token = Jifty::Plugin::OAuth::Model::RequestToken->new(current_user => Jifty::CurrentUser->superuser); ($ok, $msg) = eval { $token->create(consumer => $consumer); }; abortmsg(401, "Unable to create a Request Token: " . $@ || $msg) if $@ || !$ok; $consumer->made_request(@oauth_params{qw/timestamp nonce/}); set oauth_response => { oauth_token => $token->token, oauth_token_secret => $token->secret }; show 'oauth/response'; } =head2 authorize The user is authorizing (or denying) a consumer's request token =cut sub authorize { my @params = qw/token callback/; abortmsg(403, "Cannot authorize tokens as an OAuthed user") if Jifty->web->current_user->is_oauthed; abortmsg(401, "Must be authenticated to authorize tokens") if !Jifty->web->current_user->id; set no_abort => 1; my %oauth_params = get_parameters(@params); set callback => $oauth_params{callback}; set consumer => 'Some application'; del 'token'; if ($oauth_params{token}) { my $request_token = Jifty::Plugin::OAuth::Model::RequestToken->new(current_user => Jifty::CurrentUser->superuser); $request_token->load_by_cols(token => $oauth_params{token}, authorized => 0); if ($request_token->id) { set consumer => $request_token->consumer; set token => $oauth_params{token}; } } } =head2 authorize_post The user is submitting an AuthorizeRequestToken action =cut sub authorize_post { abortmsg(403, "Cannot authorize tokens as an OAuthed user") if Jifty->web->current_user->is_oauthed; my $result = Jifty->web->response->result("authorize_request_token"); unless ($result && $result->success) { redirect '/oauth/authorize'; } set result => $result; show '/oauth/authorized'; } =head2 access_token The consumer is trying to trade a request token for an access token =cut sub access_token { my @params = qw/consumer_key signature_method signature timestamp nonce token version/; set no_abort => 0; my %oauth_params = get_parameters(@params); my $consumer = get_consumer($oauth_params{consumer_key}); my $signature_key = get_signature_key($oauth_params{signature_method}, $consumer); my ($ok, $msg) = $consumer->is_valid_request(@oauth_params{qw/timestamp nonce/}); abortmsg(401, $msg) if !$ok; # is the request token they're using still valid? my $request_token = Jifty::Plugin::OAuth::Model::RequestToken->new(current_user => Jifty::CurrentUser->superuser); $request_token->load_by_cols(consumer => $consumer, token => $oauth_params{token}); abortmsg(401, "No token found for consumer ".$consumer->name." with key $oauth_params{token}") unless $request_token->id; ($ok, $msg) = $request_token->can_trade_for_access_token; abortmsg(401, "Cannot trade request token for access token: $msg") if !$ok; # Net::OAuth::Request will die hard if it doesn't get everything it wants my $request = eval { Net::OAuth::AccessTokenRequest->new( request_url => Jifty->web->url(path => '/oauth/access_token'), request_method => Jifty->handler->apache->method(), consumer_secret => $consumer->secret, token_secret => $request_token->secret, signature_key => $signature_key, map { $_ => $oauth_params{$_} } @params ) }; abortmsg(400, "Unable to create AccessTokenRequest: $@") if $@ || !defined($request); # make sure the signature matches the rest of what the consumer gave us abortmsg(401, "Invalid signature (type: $oauth_params{signature_method}).") unless $request->verify; my $token = Jifty::Plugin::OAuth::Model::AccessToken->create_from_request_token($request_token); abortmsg(401, "Unable to create an Access Token: " . $@ || $msg) if $@ || !defined($token) || !$ok; $consumer->made_request(@oauth_params{qw/timestamp nonce/}); $request_token->set_used(1); set oauth_response => { oauth_token => $token->token, oauth_token_secret => $token->secret }; show 'oauth/response'; } =head2 try_oauth If this is a protected resource request, see if we can authorize the request with an access token. This is dissimilar to the other OAuth requests because if anything fails, you just don't set a current_user, and then the rest of the dispatcher rules will take care of it. Thus, failure is handled quite differently in this rule. We try to abort as early as possible to make OAuth less of a hit on all requests. =cut sub try_oauth { my @params = qw/consumer_key signature_method signature timestamp nonce token version/; set no_abort => 1; my %oauth_params = get_parameters(@params); for (@params) { abortmsg(undef, "Undefined required parameter: $_"), return if !defined($oauth_params{$_}); } my $consumer = get_consumer($oauth_params{consumer_key}); return if !$consumer->id; abortmsg(undef, "No known consumer with key $oauth_params{consumer_key}"), return unless $consumer->id; my $signature_key = get_signature_key($oauth_params{signature_method}, $consumer); if ($signature_key && ref($signature_key) && !defined($$signature_key)) { abortmsg(undef, "Failed to get signature key."); return; } my ($ok, $msg) = $consumer->is_valid_request(@oauth_params{qw/timestamp nonce/}); abortmsg(undef, $msg), return if !$ok; my $access_token = Jifty::Plugin::OAuth::Model::AccessToken->new(current_user => Jifty::CurrentUser->superuser); $access_token->load_by_cols(consumer => $consumer, token => $oauth_params{token}); abortmsg(undef, "No token found for consumer ".$consumer->name." with key $oauth_params{token}"), return unless $access_token->id; ($ok, $msg) = $access_token->is_valid; abortmsg(undef, "Cannot access protected resources with this access token: $msg"), return if !$ok; # Net::OAuth::Request will die hard if it doesn't get everything it wants my $request = eval { Net::OAuth::ProtectedResourceRequest->new( request_url => Jifty->web->url(path => Jifty->web->request->path), request_method => Jifty->handler->apache->method(), consumer_secret => $consumer->secret, token_secret => $access_token->secret, signature_key => $signature_key, map { $_ => $oauth_params{$_} } @params ) }; abortmsg(undef, "Unable to create ProtectedResourceRequest: $@"), return if $@ || !defined($request); abortmsg(undef, "Invalid signature (type: $oauth_params{signature_method})."), return unless $request->verify; $consumer->made_request(@oauth_params{qw/timestamp nonce/}); my $new_current_user = Jifty->app_class('CurrentUser')->new( id => $access_token->auth_as, ); $new_current_user->is_oauthed(1); $new_current_user->oauth_token($access_token); Jifty->web->temporary_current_user($new_current_user); Jifty->log->info("Consumer " . $consumer->name . " successfully OAuthed as user ". $access_token->auth_as); } =head2 invalid_method This aborts the request with an "invalid HTTP method" response code. =cut sub invalid_method { Jifty->web->response->add_header(Allow => 'POST'); abort(405); } =head2 get_consumer CONSUMER KEY Helper function to load a consumer by consumer key. Will abort if the key is unknown. =cut sub get_consumer { my $key = shift; my $consumer = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser); $consumer->load_by_cols(consumer_key => $key); abortmsg(401, "No known consumer with key $key") unless $consumer->id || get 'no_abort'; return $consumer; } =head2 get_signature_key SIGNATURE METHOD, CONSUMER Figures out the signature key for this consumer. Will abort if the signature method is unsupported, or if the consumer lacks the prerequisites for this signature method. Will return C if the signature key is consumer independent, as is the case for C and C<HMAC-SHA1>. C<RSA-SHA1> depends on the consumer having the C<rsa_key> field. If the signature method is invalid and no_abort is set, it will return a special value of a reference to undef. Yes this sucks but undef already has an important meaning. =cut { my %valid_signature_methods = map { $_ => 1 } qw/PLAINTEXT HMAC-SHA1 /; if (eval { require Crypt::OpenSSL::RSA; 1 }) { $valid_signature_methods{"RSA-SHA1"} = 1; } else { Jifty->log->debug("RSA-SHA1 support for OAuth unavailable: Crypt::OpenSSL::RSA is not installed."); } my %key_field = ('RSA-SHA1' => 'rsa_key'); sub get_signature_key { my ($method, $consumer) = @_; if (!$valid_signature_methods{$method}) { abortmsg(400, "Unsupported signature method requested: $method") unless get 'no_abort'; return \undef; } my $field = $key_field{$method}; # this MUST return undef if the signature method requires no prior key return undef if !defined($field); my $key = $consumer->$field; if (!defined $key) { abortmsg(400, "Consumer does not have necessary field $field required for signature method $method") unless get 'no_abort'; return; } if ($method eq 'RSA-SHA1') { $key = Crypt::OpenSSL::RSA->new_public_key($key); } return $key; } } =head2 get_parameters REQUIRED PARAMETERS This will retrieve all the request paremeters. This gets parameters besides the ones in the OAuth spec, because the signature is based on all such request parameters. Pass in by name all the OAuth-required parameters. Do not include the C<oauth_> prefix. The precedence of parameters, from highest priority to lowest priority, is: =over 4 =item Authorization header =item WWW-Authenticate header =item POST parameters =item GET parameters (aka URL's query string) =back =cut sub get_parameters { my %p; my %params = Jifty->handler->apache->params(); # Check Authorization header my $authz = Jifty->handler->apache->header_in("Authorization"); if ($authz && $authz =~ s/^\s*OAuth\s*//i) { while ($authz =~ m{\s*([%a-zA-Z0-9._~-]+)="([%a-zA-Z0-9._~-]*)"\s*}g) { $params{uri_unescape($1)} = uri_unescape($2); } } for (@_) { $p{$_} = delete $params{"oauth_$_"} if !defined $p{$_}; } $p{version} ||= '1.0'; unless (get 'no_abort') { # check to see if there are any unsupported parameters while (my ($key, undef) = each %params) { abortmsg(400, "Unsupported parameter: $key") if $key =~ /^oauth_/; } # check to see if we're missing anything for (@_) { abortmsg(400, "Undefined required parameter: $_") if !defined($p{$_}); } if ($p{timestamp} && $p{timestamp} !~ /^\d+$/) { abortmsg(400, "Malformed timestamp. Expected positive integer, got $p{timestamp}"); } } return %p; } 1; ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/lib/Jifty/Plugin/OAuth/Model/�����������������������������������������������0000755�0000765�0000765�00000000000�11301114516�021504� 5����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/lib/Jifty/Plugin/OAuth/Model/AccessToken.pm���������������������������������0000644�0000765�0000765�00000005164�11174267043�024266� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env perl package Jifty::Plugin::OAuth::Model::AccessToken; use strict; use warnings; use base qw( Jifty::Plugin::OAuth::Token Jifty::Record ); use constant is_private => 1; # kludge 1: you cannot call Jifty->app_class within schema {} # kludge 3: due to the loading order, you can't really do this #my $app_user; #BEGIN { $app_user = Jifty->app_class('Model', 'User') } use Jifty::DBI::Schema; use Jifty::Record schema { # kludge 2: this kind of plugin cannot yet casually refer_to app models column auth_as => type is 'integer'; #refers_to $app_user; column valid_until => type is 'timestamp', filters are 'Jifty::DBI::Filter::DateTime'; column token => type is 'varchar', is required; column secret => type is 'varchar', is required; column consumer => refers_to Jifty::Plugin::OAuth::Model::Consumer; column can_write => is boolean; }; =head2 table AccessTokens are stored in the table C<oauth_access_tokens>. =cut sub table {'oauth_access_tokens'} =head2 create_from_request_token This creates a new access token (as the superuser) and populates its values from the given request token. =cut sub create_from_request_token { my $self = shift; my $request_token = shift; if (!ref($self)) { $self = $self->new(current_user => Jifty::CurrentUser->superuser); } my $restrictions = $request_token->access_token_restrictions or die "No access-token restrictions given in the request token."; $self->create( consumer => $request_token->consumer, auth_as => $request_token->authorized_by, valid_until => $restrictions->{use_limit}, can_write => $restrictions->{can_write} ? 1 : 0, ); return $self; } =head2 is_valid This neatly encapsulates the "is this access token perfect?" check. This will return a (boolean, message) pair, with boolean indicating success (true means the token is good) and message indicating error (or another affirmation of success). =cut sub is_valid { my $self = shift; return (0, "Access token has no authorizing user") if !$self->auth_as; return (0, "Access token expired") if $self->valid_until < DateTime->now; return (1, "Request token valid"); } =head2 current_user_can Only root may have access to this model. In the near future, we should allow the authorizing user to edit this token (taking care of course that the authorizing user is not actually authed via OAuth!) =cut sub current_user_can { my $self = shift; return $self->current_user->is_superuser; } 1; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/lib/Jifty/Plugin/OAuth/Model/Consumer.pm������������������������������������0000644�0000765�0000765�00000007254�11174267043�023661� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env perl package Jifty::Plugin::OAuth::Model::Consumer; use strict; use warnings; use base qw( Jifty::Record ); use constant is_private => 1; use Jifty::DBI::Schema; use Jifty::Record schema { # the unique key that identifies a consumer column consumer_key => type is 'varchar', is distinct, is required; # a secret used in signing to verify that we have the real consumer (and # not just someone who got ahold of the key) column secret => type is 'varchar', is required; # the name of the consumer, e.g. Bob's Social Network column name => type is 'varchar', is required; # the url of the consumer, e.g. http://social.bob/ column url => type is 'varchar'; column rsa_key => type is 'varchar', hints are 'This is only necessary if you want to support RSA-SHA1 signatures'; # we use these to make sure we aren't being hit with a replay attack column last_timestamp => type is 'integer', is required, default is 0; column nonces => type is 'blob', filters are 'Jifty::DBI::Filter::Storable'; }; =head2 table Consumers are stored in the table C<oauth_consumers>. =cut sub table {'oauth_consumers'} =head2 before_set_last_timestamp If the new timestamp is different from the last_timestamp, then clear any nonces we've used. Nonces must only be unique for requests of a given timestamp. Note that you should ALWAYS call is_valid_request before updating the last_timestamp. You should also verify the signature and make sure the request all went through before updating the last_timestamp. Otherwise an attacker may be able to create a request with an extraordinarily high timestamp and screw up the regular consumer. =cut sub before_set_last_timestamp { my $self = shift; my $new_ts = shift->{value}; # uh oh, looks like sloppy coding.. if ($new_ts < $self->last_timestamp) { die "The new timestamp is LESS than the last timestamp. You forgot to call is_valid_request!"; } # if this is a new timestamp, then flush the nonces if ($new_ts != $self->last_timestamp) { $self->set_nonces( {} ); } } =head2 is_valid_request TIMESTAMP, NONCE This will do some sanity checks (as required for security by the OAuth spec). It will make sure that the timestamp is not less than the latest timestamp for this consumer. It will also make sure that the nonce hasn't been seen for this timestamp (very important). ALWAYS call this method when handling OAuth requests. EARLY. =cut sub is_valid_request { my ($self, $timestamp, $nonce) = @_; return (0, "Timestamp nonincreasing, $timestamp < ".$self->last_timestamp.".") if $timestamp < $self->last_timestamp; return 1 if $timestamp > $self->last_timestamp; # if this is the same timestamp as the last, we must check that the nonce # is unique across the requests of these timestamps return (0, "Already used the nonce $nonce.") if defined $self->nonces->{$nonce}; return 1; } =head2 made_request TIMESTAMP, NONCE This method is to be called just before you're done processing an OAuth request. Parameters were valid, no errors occurred, everything's generally hunky-dory. This updates the C<last_timestamp> of the consumer, and sets the nonce as "used" for this new timestamp. =cut sub made_request { my ($self, $timestamp, $nonce) = @_; $self->set_last_timestamp($timestamp); $self->set_nonces({ %{$self->nonces}, $nonce => 1 }); } =head2 current_user_can Only root may have access to this model. =cut sub current_user_can { my $self = shift; return $self->current_user->is_superuser; } 1; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/lib/Jifty/Plugin/OAuth/Model/RequestToken.pm��������������������������������0000644�0000765�0000765�00000004640�11174267043�024513� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env perl package Jifty::Plugin::OAuth::Model::RequestToken; use strict; use warnings; use base qw( Jifty::Plugin::OAuth::Token Jifty::Record ); use constant is_private => 1; # kludge 1: you cannot call Jifty->app_class within schema {} # kludge 3: due to the loading order, you can't really do this #my $app_user; #BEGIN { $app_user = Jifty->app_class('Model', 'User') } use Jifty::DBI::Schema; use Jifty::Record schema { column valid_until => type is 'timestamp', filters are 'Jifty::DBI::Filter::DateTime', is required; column authorized => is boolean; # kludge 2: this kind of plugin cannot yet casually refer_to app models column authorized_by => type is 'integer'; #refers_to $app_user; column consumer => refers_to Jifty::Plugin::OAuth::Model::Consumer, is required; column used => is boolean; column token => type is 'varchar', is required; column secret => type is 'varchar', is required; column access_token_restrictions => type is 'blob', filters are 'Jifty::DBI::Filter::Storable'; }; =head2 table RequestTokens are stored in the table C<oauth_request_tokens>. =cut sub table {'oauth_request_tokens'} =head2 after_set_authorized This will set the C<authorized_by> to the current user. It will also refresh the valid_until to be active for another hour. =cut sub after_set_authorized { my $self = shift; $self->set_authorized_by(Jifty->web->current_user->id); } =head2 can_trade_for_access_token This neatly encapsulates the "is this request token perfect?" check. This will return a (boolean, message) pair, with boolean indicating success (true means the token is good) and message indicating error (or another affirmation of success). =cut sub can_trade_for_access_token { my $self = shift; return (0, "Request token is not authorized") if !$self->authorized; return (0, "Request token does not have an authorizing user") if !$self->authorized_by; return (0, "Request token already used") if $self->used; return (0, "Request token expired") if $self->valid_until < DateTime->now; return (1, "Request token valid"); } =head2 current_user_can Only root may have access to this model. =cut sub current_user_can { my $self = shift; return $self->current_user->is_superuser; } 1; ������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/lib/Jifty/Plugin/OAuth/Token.pm���������������������������������������������0000644�0000765�0000765�00000003004�11174267043�022073� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env perl package Jifty::Plugin::OAuth::Token; use strict; use warnings; use Scalar::Util 'blessed'; =head1 DESCRIPTION This just provides some helper methods for both token classes to use =cut =head2 generate_token This will create a randomly generated 20-character token for use as a request or access token. The string is hexadecimal. This does not check for uniqueness. =cut sub generate_token { return join '', map { unpack('H2', chr(int rand 256)) } 1..10; } =head2 before_create This does some checks and provides some defaults. It tries a number of times to create a unique C<token> using C<generate_token>. If that fails, this method will DIE. It will also create a secret using C<generate_token>. Finally, it will create a default C<valid_until> of 1 hour from now. =cut sub before_create { my ($self, $attr) = @_; # attempt 20 times to create a unique token string for (1..20) { $attr->{token} = generate_token(); my $token = $self->new(current_user => Jifty::CurrentUser->superuser); $token->load_by_cols(token => $attr->{token}); last if !$token->id; delete $attr->{token}; } if (!defined $attr->{token}) { die "Failed 20 times to create a unique token. Giving up."; return; } # generate a secret. need not be unique, just hard to guess $attr->{secret} = generate_token(); # default the lifetime of this token to 1 hour $attr->{valid_until} ||= DateTime->now->add(hours => 1); return 1; } 1; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/lib/Jifty/Plugin/OAuth/View.pm����������������������������������������������0000644�0000765�0000765�00000015253�11174267043�021736� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Jifty::Plugin::OAuth::View; use strict; use warnings; use Jifty::View::Declare -base; =head1 NAME Jifty::Plugin::OAuth::View - Views for OAuth-ey bits =cut =head2 oauth/response Internal template. Do not use. It returns OAuth parameters to the consumer in the HTTP response body. =cut template 'oauth/response' => sub { my $params = get 'oauth_response'; if (ref($params) eq 'HASH') { outs_raw join '&', map { sprintf '%s=%s', map { Jifty->web->escape_uri($_) } $_, $params->{$_} } keys %$params; } }; =head2 oauth An OAuth description page very much geared towards Consumers, since they'll most likely be the only ones visiting yourapp.com/oauth =cut template 'oauth' => page { title => 'OAuth', subtitle => 'Information', } content { p { b { hyperlink( url => "http://oauth.net/", label => "OAuth", target => "_blank", ) }; outs " is an open protocol to allow secure authentication to users' private data. It's far more secure than users giving out their passwords." } h2 { "Users" } p { "OAuth is nearly transparent to end users. Through OAuth, other applications can have secure -- and time-limited -- read and write access to your data on this site." } p { outs "Applications may ask you to "; hyperlink( label => "authorize a 'token' on our site", url => Jifty->web->url(path => '/oauth/authorize'), ); outs ". This is normal. We want to make sure you approve of other people looking at your data."; } h2 { "Consumers" } p { "This application supports OAuth. If you'd like to access the private resources of users of this site, you must first establish a Consumer Key, Consumer Secret, and, if applicable, RSA public key with us. You can do so by contacting " . (Jifty->config->framework('AdminEmail')||'us') . "."; } p { "Once you have a Consumer Key and Consumer Secret, you may begin letting users grant you access to our site. The relevant URLs are:" } dl { dt { "Request a Request Token" } dd { Jifty->web->url(path => '/oauth/request_token') } dt { "Obtain user authorization for a Request Token" } dd { Jifty->web->url(path => '/oauth/authorize') } dt { "Exchange a Request Token for an Access Token" } dd { Jifty->web->url(path => '/oauth/access_token') } } p { my $restful = 0; for (@{ Jifty->config->framework('Plugins') }) { if (defined $_->{REST}) { $restful = 1; last; } } outs "While you have a valid access token, you may browse the site as the user normally does."; if ($restful) { outs " You may also use "; hyperlink( url => Jifty->web->url(path => '=/help'), label => "our REST interface", target => "_blank", ); outs "."; } } }; =head2 oauth/authorize This is the page that Users see when authorizing a request token. It renders the "insert token here" textbox if the consumer didn't put the request token in the GET query, and (always) renders Allow/Deny buttons. =cut template 'oauth/authorize' => page { title => 'OAuth', subtitle => 'Someone wants stuff!', } content { show '/oauth/help'; my $authorize = Jifty->web->new_action( moniker => 'authorize_request_token', class => 'AuthorizeRequestToken', ); Jifty->web->form->start(); # if the site put the token in the request, then use it # otherwise, prompt the user for it my %args; my $token = get 'token'; if ($token) { $args{token} = $token; } else { $authorize->form_field('token')->render; } $authorize->form_field('use_limit')->render; $authorize->form_field('can_write')->render; outs_raw $authorize->hidden(callback => get 'callback'); outs_raw($authorize->button( label => 'Deny', arguments => { %args, authorize => 'deny' }, )); outs_raw($authorize->button( label => 'Allow', arguments => { %args, authorize => 'allow' }, )); Jifty->web->form->end(); }; =head2 oauth/authorized Displayed after the user authorizes or denies a request token. Uses a link to the callback if provided, otherwise the site's URL. =cut template 'oauth/authorized' => page { title => 'OAuth', subtitle => 'Authorized', } content { my $result = get 'result'; my $callback = $result->content('callback'); my $token = $result->content('token'); my $token_obj = $result->content('token_obj'); $callback ||= $token_obj->consumer->url; if (!$callback) { p { "Oops! " . $token_obj->consumer->name . " didn't tell us how to get you back to their service. If you do find your way back, you'll probably need this token: " . $token }; } else { $callback .= ($callback =~ /\?/ ? '&' : '?') . 'oauth_token=' . $token; set consumer => $token_obj->consumer; p { outs 'To return to '; show '/oauth/consumer'; outs ', '; hyperlink( label => 'click here', url => $callback, ); outs '.'; }; } }; =head2 oauth/help This provides a very, very layman description of OAuth for users =cut private template 'oauth/help' => sub { div { p { show '/oauth/consumer'; outs ' is trying to access your data on this site. If you trust this application, you may grant it access.'; } p { "If you're at all uncomfortable with the idea of someone rifling through your things, or don't know what this is, click Deny." } p { hyperlink( label => "Learn more about OAuth.", url => "http://oauth.net/", target => "_blank", ) } } }; =head2 oauth/consumer Renders the consumer's name, and if available, its URL as a link. =cut private template 'oauth/consumer' => sub { my $consumer = (get 'consumer') || 'Some application'; span { outs ref($consumer) ? $consumer->name : $consumer; if (ref($consumer) && $consumer->url) { outs ' <'; hyperlink( url => $consumer->url, label => $consumer->url, target => "_blank", ); outs ' >'; } } }; 1; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/lib/Jifty/Plugin/OAuth.pm���������������������������������������������������0000644�0000765�0000765�00000020075�11301114373�021007� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Jifty::Plugin::OAuth; use strict; use warnings; use base qw/Jifty::Plugin/; our $VERSION = '0.04'; sub init { Jifty::CurrentUser->mk_accessors(qw(is_oauthed oauth_token)); Jifty::Record->add_trigger(before_access => sub { my $record = shift; my $right = shift; # not oauthed, so use default $record->current_user->is_oauthed or return 'ignore'; my $token = $record->current_user->oauth_token; # OAuthed users have no read restrictions, so use default return 'ignore' if $right eq 'read'; # token gives write access, so use default return 'ignore' if $token->__value('can_write'); # we have been forbidden from writing! Jifty->log->error("Unable to $right " . ref($record) . " " . ($record->id||'new') . " because the OAuth access token does not allow it."); return 'deny'; }); for my $type (qw/create set delete/) { Jifty::DBI::Record->add_trigger( abortable => 1, name => "before_$type", callback => sub { my $record = shift; # not a Jifty::Object, so allow write $record->can('current_user') or return 1; # not oauthed, so allow write $record->current_user->is_oauthed or return 1; my $token = $record->current_user->oauth_token; # token gives write access, so allow write return 1 if $token->__value('can_write'); # we have been forbidden from writing! Jifty->log->debug("Unable to $type " . ref($record) . " " . ($record->id||'new') . " because the OAuth access token does not allow it."); my $ret = Class::ReturnValue->new; $ret->as_array(0, "Your OAuth access token denies you write access."); $ret->as_error( errno => 1, message => 'Your OAuth access token denies you write access.', ); return $ret->return_value; }, ); } } 1; __END__ =head1 NAME Jifty::Plugin::OAuth - secure API authorization =head1 DESCRIPTION An OAuth web services API for your Jifty app. Users may grant B<limited> authorization to other applications in a secure way. This plugin adds an C</oauth> set of URLs to your application, listed below. It also adds C<is_oauthed> and C<oauth_token> to L<Jifty::CurrentUser>, so you may have additional restrictions on OAuth access (such as forbidding OAuthed users to change users' passwords). =head2 /oauth This lists some basic information about OAuth, and where to learn more. It also tells consumers how they may gain OAuth-ability for your site. =head2 /oauth/request_token The URL at which consumers POST to get a request token =head2 /oauth/authorize The URL at which users authorize request tokens =head2 /oauth/authorized After authorizing or denying a request token, users are directed here before going back to the consumer's site =head2 /oauth/access_token The URL that consumers POST to trade an authorized request token for an access token, with which they may act on the user's behalf =head1 WARNING This plugin is beta. Please let us know if there are any issues with it. =head1 USAGE Add the following to your config: framework: Plugins: - OAuth: {} =head1 GLOSSARY =over 4 =item service provider A service provider is an application that has users who have private data. This plugin enables your Jifty application to be an OAuth service provider. =item consumer A consumer is an application that wants to access or change users' private data. The service provider (in this case, this plugin) ensures that this happens securely and with users' full approval. Without OAuth (or similar systems), this would be accomplished perhaps by the user giving the consumer her login information. Obviously not ideal. This plugin does not implement the protocol as a consumer, only as a service provider. You'll likely want more control and flexibility as a consumer. =item request token A request token is a unique, random string that a user may authorize for a consumer. =item access token An access token is a unique, random string that a consumer can use to access private resources on the authorizing user's behalf. Consumers may only receive an access token if they have an authorized request token. =back =head1 NOTES You must provide consumers access to C</oauth/request_token> and C</oauth/access_token>. You could restrict C</oauth/request_token> and C</oauth/access_token> to only logged-in users and require consumers to log in. Perhaps you could have a column in your users table that represents whether this user is a consumer and restrict access to these URLs that way. Or, you could let anyone access these URLs. This policy is left you as the developer. Limiting access is wise because each hit to C</oauth/request_token> and C</oauth/access_token> uses some entropy (so an attacker could run a denial-of-service attack against you) You should not allow public access to C</oauth/authorize>. C</oauth/authorize> will throw a 401 (unauthorized) error if an unauthenticated user accesses it. On unauthenticated access of C</oauth/authorize>, you should tangent the user to your login page to improve usability. You should allow public access to C</oauth>. This has some information for consumers. There is currently no way for consumers to add themselves. This might change in the future, with an OAuth extension. Consumers must contact you and provide you with the following data: =over 4 =item consumer_key An arbitrary string that uniquely identifies a consumer. Preferably something random over, say, "Hiveminder". =item secret A (preferably random) string that is used to ensure that it's really the consumer you're talking to. After the consumer provides this to you, it's never sent in plaintext. It is always, however, included in cryptographic signatures. =item name A readable name to use in displaying the consumer to users. This is where you'd put "Hiveminder". =item url (optional) The website of the consumer. =item rsa_key (optional) The consumer's public RSA key. This is optional. Without it, they will not be able to use the RSA-SHA1 signature method. They can still use HMAC-SHA1 though. =back =head1 TECHNICAL DETAILS OAuth is an open protocol that enables consumers to access users' private data in a secure and authorized manner. The way it works is: =over 4 =item The consumer establishes a key and a secret with the service provider. This step only happens once, and is currently manual. =item The user is using the consumer's application and decides that she wants to use some data that she already has on the service provider's application. =item The consumer asks the service provider for a request token. The service provider generates one and gives it to the consumer. =item The consumer directs the user to the service provider with that request token. =item The user logs in and authorizes that request token. =item The service provider directs the user back to the consumer. =item The consumer asks the service provider to exchange his authorized request token for an access token. This access token lets the consumer access resources on the user's behalf in a limited way, for a limited amount of time. =back By establishing secrets and using signatures and timestamps, this can be done in a very secure manner. For example, a replay attack (an eavesdropper repeats a request made by a legitimate consumer) is actively defended against. =head1 METHODS =head2 init This adds an is_oauthed accessor to L<Jifty::CurrentUser>. It also establishes a trigger in L<Jifty::Record> so that only OAuthed consumers with write access can do anything other than read. =head1 SEE ALSO L<Net::OAuth::Request>, L<http://oauth.net/> =head1 AUTHOR Shawn M Moore C<< <sartak@bestpractical.com> >> =head1 LICENSE Jifty::Plugin::OAuth is Copyright 2007-2008 Best Practical Solutions, LLC. Jifty::Plugin::OAuth is distributed under the same terms as Perl itself. =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/Makefile.PL�����������������������������������������������������������������0000644�0000765�0000765�00000000720�11174267043�016320� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������use inc::Module::Install; name 'Jifty-Plugin-OAuth'; all_from 'lib/Jifty/Plugin/OAuth.pm'; requires('Jifty' => '0.80408'); requires('Net::OAuth::Request' => '0.05'); features( 'RSA signing' => [ -default => 0, recommends('Crypt::OpenSSL::RSA'), ], 'HMAC signing' => [ -default => 1, recommends('Digest::HMAC_SHA1'), ], ); build_requires 'Test::More'; tests('t/t/*.t'); auto_install; WriteAll; ������������������������������������������������Jifty-Plugin-OAuth-0.04/MANIFEST��������������������������������������������������������������������0000644�0000765�0000765�00000002134�11301114451�015462� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Changes inc/Module/AutoInstall.pm inc/Module/Install.pm inc/Module/Install/AutoInstall.pm inc/Module/Install/Base.pm inc/Module/Install/Can.pm inc/Module/Install/Fetch.pm inc/Module/Install/Include.pm inc/Module/Install/Makefile.pm inc/Module/Install/Metadata.pm inc/Module/Install/Win32.pm inc/Module/Install/WriteAll.pm lib/Jifty/Plugin/OAuth.pm lib/Jifty/Plugin/OAuth/Action/AuthorizeRequestToken.pm lib/Jifty/Plugin/OAuth/Dispatcher.pm lib/Jifty/Plugin/OAuth/Model/AccessToken.pm lib/Jifty/Plugin/OAuth/Model/Consumer.pm lib/Jifty/Plugin/OAuth/Model/RequestToken.pm lib/Jifty/Plugin/OAuth/Token.pm lib/Jifty/Plugin/OAuth/View.pm Makefile.PL MANIFEST This list of files META.yml t/bin/jifty t/etc/config.yml t/lib/TestApp/Plugin/OAuth/Dispatcher.pm t/lib/TestApp/Plugin/OAuth/Model/Favorite.pm t/lib/TestApp/Plugin/OAuth/Model/User.pm t/lib/TestApp/Plugin/OAuth/Test.pm t/lib/TestApp/Plugin/OAuth/View.pm t/Makefile.PL t/t/00-test-setup.t t/t/01-basic.t t/t/02-request-token.t t/t/03-authorize.t t/t/04-access-token.t t/t/05-protected-resource.t t/t/06-read-only.t t/t/07-read-write.t t/t/id_rsa t/t/id_rsa.pub ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/META.yml��������������������������������������������������������������������0000644�0000765�0000765�00000001170�11301114416�015602� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������--- abstract: 'secure API authorization' author: - 'Shawn M Moore C<< <sartak@bestpractical.com> >>' build_requires: ExtUtils::MakeMaker: 6.42 Test::More: 0 configure_requires: ExtUtils::MakeMaker: 6.42 distribution_type: module generated_by: 'Module::Install version 0.91' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Jifty-Plugin-OAuth no_index: directory: - inc - t recommends: Crypt::OpenSSL::RSA: 0 Digest::HMAC_SHA1: 0 requires: Jifty: 0.80408 Net::OAuth::Request: 0.05 resources: license: http://dev.perl.org/licenses/ version: 0.04 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/��������������������������������������������������������������������������0000755�0000765�0000765�00000000000�11301114516�014576� 5����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/bin/����������������������������������������������������������������������0000755�0000765�0000765�00000000000�11301114516�015346� 5����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/bin/jifty�����������������������������������������������������������������0000755�0000765�0000765�00000000325�11174267043�016435� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env perl use warnings; use strict; use File::Basename qw(dirname); use UNIVERSAL::require; use Jifty; use Jifty::Script; local $SIG{INT} = sub { warn "Stopped\n"; exit; }; Jifty::Script->dispatch(); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/etc/����������������������������������������������������������������������0000755�0000765�0000765�00000000000�11301114516�015351� 5����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/etc/config.yml������������������������������������������������������������0000644�0000765�0000765�00000002071�11174267043�017355� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������--- framework: AdminMode: 1 ApplicationClass: TestApp::Plugin::OAuth ApplicationName: TestApp-Plugin-OAuth ApplicationUUID: B5461398-7DC0-11DC-83A6-036B06D64C5E ConfigFileVersion: 2 Database: CheckSchema: 1 Database: testapp_plugin_oauth Driver: SQLite Host: localhost Password: '' RecordBaseClass: Jifty::DBI::Record::Cachable User: '' Version: 0.0.1 DevelMode: 0 L10N: PoDir: share/po LogLevel: INFO Mailer: Sendmail MailerArgs: [] Plugins: - REST: {} - Halo: {} - CompressedCSSandJS: {} - AdminUI: {} - OAuth: {} - Authentication::Password: {} - SkeletonApp: {} PubSub: Backend: Memcached Enable: ~ SkipAccessControl: 0 TemplateClass: TestApp::Plugin::OAuth::View Web: BaseURL: http://localhost DataDir: var/mason Globals: [] MasonConfig: autoflush: 0 default_escape_flags: h error_format: text error_mode: fatal Port: 8888 ServeStaticFiles: 1 StaticRoot: share/web/static TemplateRoot: share/web/templates �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/lib/����������������������������������������������������������������������0000755�0000765�0000765�00000000000�11301114516�015344� 5����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/lib/TestApp/��������������������������������������������������������������0000755�0000765�0000765�00000000000�11301114516�016724� 5����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/lib/TestApp/Plugin/�������������������������������������������������������0000755�0000765�0000765�00000000000�11301114516�020162� 5����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/lib/TestApp/Plugin/OAuth/�������������������������������������������������0000755�0000765�0000765�00000000000�11301114516�021202� 5����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/lib/TestApp/Plugin/OAuth/Dispatcher.pm������������������������������������0000644�0000765�0000765�00000000630�11174267043�023641� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package TestApp::Plugin::OAuth::Dispatcher; use strict; use warnings; use Jifty::Dispatcher -base; my @login_required = qw{ nuke/? =/? }; my $login_required = join '|', map {"^$_"} @login_required; $login_required = qr/($login_required)/; before '**' => run { my $path = $1; if (!Jifty->web->current_user->user_object && $path =~ $login_required) { tangent '/login'; } }; 1; ��������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/lib/TestApp/Plugin/OAuth/Model/�������������������������������������������0000755�0000765�0000765�00000000000�11301114516�022242� 5����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/lib/TestApp/Plugin/OAuth/Model/Favorite.pm��������������������������������0000644�0000765�0000765�00000002033�11174267043�024371� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; package TestApp::Plugin::OAuth::Model::Favorite; use Jifty::DBI::Schema; use TestApp::Plugin::OAuth::Record schema { column 'owner' => refers_to TestApp::Plugin::OAuth::Model::User; column 'thing' => type is 'text'; }; # you only create favorites for yourself sub before_create { my $self = shift; my $args = shift; $args->{owner} = Jifty->web->current_user->user_object; return 1; } sub current_user_can { my $self = shift; my $right = shift; # all can read return 1 if $right eq 'read'; # logged in users can create return Jifty->web->current_user->user_object if $right eq 'create'; # only the owner may update his favorites return 0 unless Jifty->web->current_user->id == $self->owner->id; # none can delete return 0 if $right eq 'delete'; # oauthed can update, non-oauthed can't return !Jifty->web->current_user->is_oauthed if $right eq 'update'; die "Favorite->current_user_can($right) check fell through"; } 1; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/lib/TestApp/Plugin/OAuth/Model/User.pm������������������������������������0000644�0000765�0000765�00000001274�11174267043�023536� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use warnings; package TestApp::Plugin::OAuth::Model::User; use Jifty::DBI::Schema; use TestApp::Plugin::OAuth::Record schema { column 'tasty' => type is 'boolean', default is 'f'; column 'favorites' => refers_to TestApp::Plugin::OAuth::Model::FavoriteCollection by 'owner'; }; use Jifty::Plugin::User::Mixin::Model::User; use Jifty::Plugin::Authentication::Password::Mixin::Model::User; sub current_user_can { my $self = shift; return 1 if $self->current_user->is_superuser; return 1 if $_[0] eq 'create'; my $id = $self->__value('id'); return 1 if $id == $self->current_user->id; $self->SUPER::current_user_can(@_); } 1; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/lib/TestApp/Plugin/OAuth/Test.pm������������������������������������������0000644�0000765�0000765�00000023105�11174267043�022474� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env perl package TestApp::Plugin::OAuth::Test; use strict; use warnings; use base qw/Jifty::Test/; use MIME::Base64; use Digest::HMAC_SHA1 'hmac_sha1'; use Jifty::Test::WWW::Mechanize; our @EXPORT = qw($timestamp $url $umech $cmech $pubkey $seckey $token_obj $server $URL response_is sign get_latest_token allow_ok deny_ok _authorize_request_token get_request_token get_authorized_token get_access_token has_rsa rsa_skip start_server); our $timestamp = 0; our $url; our $umech; our $cmech; our $pubkey = slurp('t/t/id_rsa.pub'); our $seckey = slurp('t/t/id_rsa'); our $token_obj; our $server; our $URL; our $can_write; sub setup { my $class = shift; $class->SUPER::setup; $class->export_to_level(1); } sub start_server { $server = Jifty::Test->make_server; $URL = $server->started_ok; $umech = Jifty::Test::WWW::Mechanize->new(); $cmech = Jifty::Test::WWW::Mechanize->new(); $url = $URL . '/oauth/request_token'; } sub response_is { ++$timestamp; my %params = ( oauth_timestamp => $timestamp, oauth_nonce => scalar(reverse $timestamp), oauth_signature_method => 'HMAC-SHA1', oauth_version => '1.0', code => 400, testname => "", method => 'POST', token_secret => '', params_in => 'method', @_, ); local $url = $URL . delete $params{url} if $params{url}; for (grep {!defined $params{$_}} keys %params) { delete $params{$_}; } my $code = delete $params{code}; my $testname = delete $params{testname} || "Response was $code"; my $no_token = delete $params{no_token}; my $method = delete $params{method}; my $params_in = delete $params{params_in}; my $token_secret = delete $params{token_secret}; my $consumer_secret = delete $params{consumer_secret} or die "consumer_secret not passed to response_is!"; if ($url =~ /access_token/) { $token_secret ||= $token_obj->secret; $params{oauth_token} ||= $token_obj->token; } $params{oauth_signature} ||= sign($method, $token_secret, $consumer_secret, %params); my $r; if ($params_in eq 'authz') { $cmech->default_header("Authorization" => authz(%params)); } if ($method eq 'GET') { my $query = join '&', map { "$_=" . Jifty->web->escape_uri($params{$_}||'') } keys %params; my $params = $params_in eq 'method' ? "?$query" : ''; $r = $cmech->get("$url$params"); } else { my $req = HTTP::Request->new( uc($method) => $url, ); if ($params_in eq 'method') { # avoid Encode complaining about undef for (values %params) { defined or $_ = ''; } my $content = Jifty->web->query_string(%params); $req->header('Content-type' => 'application/x-www-form-urlencoded'); $req->content($content); } $r = $cmech->request($req); } $cmech->default_headers->remove_header("Authorization"); local $Test::Builder::Level = $Test::Builder::Level + 1; main::is($r->code, $code, $testname); if ($url =~ /oauth/) { undef $token_obj; get_latest_token(); if ($no_token || $code != 200) { main::ok(!$token_obj, "Did not get a token"); } elsif ($code == 200) { main::ok($token_obj, "Successfully loaded a token object with token ".$token_obj->token."."); } } return $cmech->content; } # creates an Authorization header sub authz { my %params = @_; return "OAuth " . join ', ', map { $_ . q{="} . Jifty->web->escape_uri($params{$_}) . q{"} } keys %params; } sub sign { my ($method, $token_secret, $consumer_secret, %params) = @_; local $url = delete $params{sign_url} || $url; my $key = delete $params{signature_key}; my $sig_method = $params{oauth_signature_method} || delete $params{_signature_method}; delete $params{oauth_signature}; if ($sig_method eq 'PLAINTEXT') { my $signature = join '&', map { Jifty->web->escape_uri($_||'') } $consumer_secret, $token_secret; return $signature; } my $normalized_request_parameters = join '&', map { "$_=" . Jifty->web->escape_uri($params{$_}||'') } sort keys %params; my $signature_base_string = join '&', map { Jifty->web->escape_uri($_||'') } uc($method), $url, $normalized_request_parameters; my $signature; if ($sig_method eq 'RSA-SHA1') { require Crypt::OpenSSL::RSA; my $pubkey = Crypt::OpenSSL::RSA->new_private_key($key); $signature = encode_base64($pubkey->sign($signature_base_string), ""); } elsif ($sig_method eq 'HMAC-SHA1') { my $key = join '&', map { Jifty->web->escape_uri($_||'') } $consumer_secret, $token_secret; my $hmac = Digest::HMAC_SHA1->new($key); $hmac->add($signature_base_string); $signature = encode_base64($hmac->digest, ''); } return ($signature, $signature_base_string, $normalized_request_parameters) if wantarray; return $signature; } sub has_rsa { eval { require Crypt::OpenSSL::RSA; 1 } } sub rsa_skip { my $count = shift || Carp::carp "You must specify a number of tests to skip."; ::skip 'Crypt::OpenSSL::RSA is required for these tests', $count unless has_rsa; } sub slurp { no warnings 'once'; my $file = shift; local $/; local @ARGV = $file; my $contents = scalar <> or die "Unable to slurp $file"; return $contents; } sub get_latest_token { my $content = $cmech->content; $content =~ s/\boauth_token=(\w+)// or return; my $token = $1; $content =~ s/\boauth_token_secret=(\w+)// or return; my $secret = $1; local $Test::Builder::Level = $Test::Builder::Level + 1; main::is($content, '&', "the output was exactly oauth_token=...&oauth_secret=..."); my $package = 'Jifty::Plugin::OAuth::Model::'; if ($cmech->uri =~ /request_token/) { $package .= 'RequestToken'; } elsif ($cmech->uri =~ /access_token/) { $package .= 'AccessToken'; } else { Jifty->log->error("Called get_latest_token, but I cannot grok the URI " . $cmech->uri); return; } $token_obj = $package->new(current_user => Jifty::CurrentUser->superuser); $token_obj->load_by_cols(token => $token); if (!$token_obj->id) { Jifty->log->error("Could not find a $package with token $token"); return; } return $token_obj; } sub allow_ok { local $Test::Builder::Level = $Test::Builder::Level + 1; my $error = _authorize_request_token('Allow'); ::fail($error), return if $error; my $name = $token_obj->consumer->name; if ($can_write) { $umech->content_contains("Allowing $name to read and write your data for 1 hour."); } else { $umech->content_contains("Allowing $name to read your data for 1 hour."); } } sub deny_ok { local $Test::Builder::Level = $Test::Builder::Level + 1; my $error = _authorize_request_token('Deny'); ::fail($error), return if $error; my $name = $token_obj->consumer->name; $umech->content_contains("Denying $name the right to access your data."); } sub _authorize_request_token { local $Test::Builder::Level = $Test::Builder::Level + 1; my $which_button = shift or die "You did not specify a button to click to _authorize_request_token"; my $token = shift || $token_obj->token; $token = $token->token if ref $token; $umech->get('/oauth/authorize') or return "Unable to navigate to /oauth/authorize";; $umech->content =~ /If you trust this application/ or return "Content did not much qr/If you trust this application/"; my $moniker = $umech->moniker_for('TestApp::Plugin::OAuth::Action::AuthorizeRequestToken') or return "Unable to find moniker for AuthorizeRequestToken"; $umech->fill_in_action($moniker, token => $token, can_write => $can_write, ) or return "Unable to fill in the AuthorizeRequestToken action"; $umech->click_button(value => $which_button) or return "Unable to click $which_button button"; return; } sub get_request_token { local $Test::Builder::Level = $Test::Builder::Level + 1; response_is( url => '/oauth/request_token', code => 200, testname => "200 - plaintext signature", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', @_, ); return $token_obj; } sub get_authorized_token { local $Test::Builder::Level = $Test::Builder::Level + 1; get_request_token(@_); allow_ok(); return $token_obj; } sub get_access_token { local $Test::Builder::Level = $Test::Builder::Level + 1; get_authorized_token() unless shift; response_is( url => '/oauth/access_token', code => 200, testname => "200 - plaintext signature", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', ); } 1; �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/lib/TestApp/Plugin/OAuth/View.pm������������������������������������������0000644�0000765�0000765�00000000416�11174267043�022467� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env perl package TestApp::Plugin::OAuth::View; use strict; use warnings; use Jifty::View::Declare -base; template '/nuke/the/whales' => page { h1 { "Press the shiny red button." } h2 { "You are human #" . Jifty->web->current_user->id . "." } }; 1; ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/Makefile.PL���������������������������������������������������������������0000644�0000765�0000765�00000000200�11174267043�016554� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������use inc::Module::Install; name 'TestApp-Plugin-OAuth'; version '0.01'; requires 'Jifty' => '0.70824'; WriteAll; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/t/������������������������������������������������������������������������0000755�0000765�0000765�00000000000�11301114516�015041� 5����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/t/00-test-setup.t���������������������������������������������������������0000644�0000765�0000765�00000006566�11174267043�017611� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 9; use lib 't/lib'; use TestApp::Plugin::OAuth::Test; # this sucks but I can't figure out why I'm getting failures # let's see if I can get any useful pointers from testers.. diag "PLUGINS: " . join ', ', sort map { keys %$_ } @{ Jifty->config->framework('Plugins') }; diag "APPROOT: " . Jifty::Util->app_root; diag "\$0: $0"; # sign PLAINTEXT {{{ is(sign('POST', 'jjd999tj88uiths3', 'djr9rjt0jd78jf88', oauth_signature_method => 'PLAINTEXT'), 'djr9rjt0jd78jf88&jjd999tj88uiths3', 'PLAINTEXT example 1 works'); is(sign('POST', 'jjd99$tj88uiths3', 'djr9rjt0jd78jf88', oauth_signature_method => 'PLAINTEXT'), 'djr9rjt0jd78jf88&jjd99%24tj88uiths3', 'PLAINTEXT example 2 works'); is(sign('POST', undef, 'djr9rjt0jd78jf88', oauth_signature_method => 'PLAINTEXT'), 'djr9rjt0jd78jf88&', 'PLAINTEXT example 2 works'); # }}} # sign HMAC-SHA1 {{{ my ($sig, $sbs, $nrp) = sign( 'GET', 'pfkkdhi9sl3r4s00', 'kd94hf93k423kf44', sign_url => 'http://photos.example.net/photos', oauth_consumer_key => 'dpf43f3p2l4k3l03', oauth_signature_method => 'HMAC-SHA1', oauth_timestamp => '1191242096', oauth_nonce => 'kllo9940pd9333jh', oauth_token => 'nnch734d00sl2jdk', file => 'vacation.jpg', size => 'original', oauth_version => '1.0'); is($nrp, 'file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original', 'HMAC-SHA1 normalized request paramaters correct'); is($sbs, 'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal', 'HMAC-SHA1 signature-base-string correct'); is($sig, 'tR3+Ty81lMeYAr/Fid0kMTYa/WM=', 'HMAC-SHA1 signature correct'); # }}} # sign RSA-SHA1 {{{ SKIP: { rsa_skip(3); ($sig, $sbs, $nrp) = sign( 'GET', 'pfkkdhi9sl3r4s00', 'kd94hf93k423kf44', sign_url => 'http://photos.example.net/photos', signature_key => $seckey, oauth_consumer_key => 'dpf43f3p2l4k3l03', oauth_signature_method => 'RSA-SHA1', oauth_timestamp => '1191242096', oauth_nonce => 'kllo9940pd9333jh', oauth_token => 'nnch734d00sl2jdk', file => 'vacation.jpg', size => 'original', oauth_version => '1.0'); is($nrp, 'file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=RSA-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original', 'RSA-SHA1 normalized request paramaters correct'); is($sbs, 'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal', 'RSA-SHA1 signature-base-string correct'); is($sig, 'NA2rGBEAnHta9amI/lwEHmuJzkDF2CtfzPNc+jbQIvsFKi0AyRQFi1etC+yxmHLn6bHKSHmn/pR4GOhN+2AP5fi0Aw9mr9n/k7LybUCUwRK/OjJH7b8ESXhkluss+UXCZoLOeaO9Pxskdi1DzWMOhY8si9hfYsCGrHrVbdcqwcw=', 'RSA-SHA1 signature correct'); } # }}} ������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/t/01-basic.t��������������������������������������������������������������0000644�0000765�0000765�00000001567�11174267043�016552� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env perl use warnings; use strict; use lib 't/lib'; use Jifty::Test tests => 9; use Jifty::Test::WWW::Mechanize; my $server = Jifty::Test->make_server; isa_ok($server, 'Jifty::Server'); my $URL = $server->started_ok; my $mech = Jifty::Test::WWW::Mechanize->new(); $mech->get_ok($URL . '/oauth'); $mech->content_like(qr{/oauth/request_token}, "oauth page mentions request_token URL"); $mech->content_like(qr{/oauth/authorize}, "oauth page mentions authorize URL"); $mech->content_like(qr{/oauth/access_token}, "oauth page mentions access_token URL"); $mech->content_like(qr{http://oauth\.net/}, "oauth page mentions OAuth homepage"); my $response = $mech->get($URL . '/oauth/authorize'); is($response->code, 401, "/oauth/authorize requires being logged in"); $mech->content_unlike(qr{If you trust this application}, "/oauth/authorize requires being logged in"); �����������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/t/02-request-token.t������������������������������������������������������0000644�0000765�0000765�00000020524�11174267043�020272� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env perl use warnings; use strict; use lib 't/lib'; use Test::More tests => 61; use TestApp::Plugin::OAuth::Test; start_server(); # create some consumers {{{ my $consumer = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser); my ($ok, $msg) = $consumer->create( consumer_key => 'foo', secret => 'bar', name => 'FooBar industries', url => 'http://foo.bar.example.com', rsa_key => $pubkey, ); ok($ok, $msg); my $rsaless = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser); ($ok, $msg) = $rsaless->create( consumer_key => 'foo2', secret => 'bar2', name => 'Backwater.org', url => 'http://backwater.org', ); ok($ok, $msg); # }}} # success modes # get a request token as a known consumer (PLAINTEXT) {{{ response_is( code => 200, testname => "200 - plaintext signature", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', ); # }}} # get a request token as a known consumer (HMAC-SHA1) {{{ $timestamp = 100; # set timestamp to test different consumers' timestamps response_is( code => 200, testname => "200 - HMAC-SHA1 signature", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'HMAC-SHA1', ); # }}} # get a request token as a known consumer (RSA-SHA1) {{{ SKIP: { rsa_skip(3); response_is( code => 200, testname => "200 - RSA-SHA1 signature", consumer_secret => 'bar', oauth_consumer_key => 'foo', signature_key => $seckey, oauth_signature_method => 'RSA-SHA1', ); }; # }}} # get a request token using authorization header {{{ response_is( code => 200, testname => "200 - Authorization header", consumer_secret => 'bar', params_in => 'authz', oauth_consumer_key => 'foo', oauth_signature_method => 'HMAC-SHA1', ); # }}} # same timestamp, different nonce {{{ --$timestamp; response_is( code => 200, testname => "200 - same timestamp, different nonce", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_nonce => 'kjfh', oauth_signature_method => 'HMAC-SHA1', ); # }}} # same nonce, different timestamp {{{ response_is( code => 200, testname => "200 - same nonce, different timestamp", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_nonce => 'kjfh', oauth_signature_method => 'HMAC-SHA1', ); # }}}} # get a request token as an RSA-less consumer (PLAINTEXT) {{{ # consumer 1 has a timestamp of about 101 now. if this gives a timestamp error, # then timestamps must be globally increasing, which is wrong. they must only # be increasing per consumer $timestamp = 50; response_is( code => 200, testname => "200 - plaintext signature", consumer_secret => 'bar2', oauth_consumer_key => 'foo2', oauth_signature_method => 'PLAINTEXT', ); # }}} # get a request token as an RSA-less consumer (HMAC-SHA1) {{{ response_is( code => 200, testname => "200 - HMAC-SHA1 signature", consumer_secret => 'bar2', oauth_consumer_key => 'foo2', oauth_signature_method => 'HMAC-SHA1', ); # }}} # failure modes # request a request token as an RSA-less consumer (RSA-SHA1) {{{ SKIP: { rsa_skip(2); response_is( code => 400, testname => "400 - RSA-SHA1 signature, without registering RSA key!", consumer_secret => 'bar2', oauth_consumer_key => 'foo2', signature_key => $seckey, oauth_signature_method => 'RSA-SHA1', ); }; # }}} # unknown consumer {{{ # we're back to the first consumer, so we need a locally larger timestamp $timestamp = 200; response_is( code => 401, testname => "401 - unknown consumer", consumer_secret => 'zzz', oauth_consumer_key => 'whoami', ); # }}} # wrong consumer secret {{{ response_is ( code => 401, testname => "401 - wrong consumer secret", consumer_secret => 'not bar!', oauth_consumer_key => 'foo', ); # }}} # wrong signature {{{ response_is( code => 401, testname => "401 - wrong signature", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature => 'hello ^____^', ); # }}} # unknown signature method {{{ response_is( code => 400, testname => "400 - unknown signature method", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'Peaches. Peaches FOR YOU', ); # }}} # missing parameters {{{ # oauth_consumer_key {{{ response_is( code => 400, testname => "400 - missing parameter oauth_consumer_key", consumer_secret => 'bar', oauth_signature_method => 'PLAINTEXT', ); # }}} # oauth_nonce {{{ response_is( code => 400, testname => "400 - missing parameter oauth_nonce", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_nonce => undef, oauth_signature_method => 'PLAINTEXT', ); # }}} # oauth_timestamp {{{ response_is( code => 400, testname => "400 - missing parameter oauth_timestamp", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_timestamp => undef, oauth_signature_method => 'PLAINTEXT', ); # }}} # oauth_signature_method {{{ response_is( code => 400, testname => "400 - missing parameter oauth_signature_method", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => undef, _signature_method => 'PLAINTEXT', # so we get a real signature ); # }}} # }}} # unsupported parameter {{{ response_is( code => 400, testname => "400 - unsupported parameter oauth_candy", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_candy => 'yummy', ); # }}} # invalid timestamp (noninteger) {{{ response_is( code => 400, testname => "400 - malformed timestamp (noninteger)", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_timestamp => 'half past nine', ); # }}} # invalid timestamp (smaller than previous request) {{{ $timestamp = 1000; # first make a good request with a large timestamp {{{ response_is( code => 200, testname => "200 - setting up a future test", consumer_secret => 'bar', oauth_consumer_key => 'foo', ); # }}} $timestamp = 500; # then a new request with a smaller timestamp {{{ response_is( code => 401, testname => "401 - timestamp smaller than a previous timestamp", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', ); # }}} $timestamp = 2000; # }}} # GET not POST {{{ response_is( code => 405, testname => "405 - GET not allowed for request_token", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', method => 'GET', ); # }}} # duplicate timestamp and nonce {{{ response_is( code => 200, testname => "200 - plaintext signature", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', ); --$timestamp; response_is( code => 401, testname => "401 - duplicate timestamp and nonce", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', ); # }}} ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/t/03-authorize.t����������������������������������������������������������0000644�0000765�0000765�00000016020�11174267043�017473� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env perl use warnings; use strict; use lib 't/lib'; use Test::More tests => 85; use TestApp::Plugin::OAuth::Test; use Jifty::Test::WWW::Mechanize; start_server(); # create some consumers {{{ my $consumer = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser); my ($ok, $msg) = $consumer->create( consumer_key => 'foo', secret => 'bar', name => 'FooBar Industries', url => 'http://foo.bar.example.com', rsa_key => $pubkey, ); ok($ok, $msg); my $rsaless = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser); ($ok, $msg) = $rsaless->create( consumer_key => 'foo2', secret => 'bar2', name => 'Backwater.org', url => 'http://backwater.org', ); ok($ok, $msg); # }}} # try to navigate to protected pages while not logged in {{{ my $response = $umech->get($URL . '/oauth/authorize'); is($response->code, 401, "/oauth/authorize requires authentication"); $umech->content_unlike(qr/If you trust this application/); $response = $umech->get('/oauth/authorized'); is($response->code, 401, "/oauth/authorized requires authentication"); $umech->content_unlike(qr/If you trust this application/); $umech->get_ok('/nuke/the/whales'); $umech->content_unlike(qr/Press the shiny red button/); # }}} # log in {{{ my $u = TestApp::Plugin::OAuth::Model::User->new(current_user => TestApp::Plugin::OAuth::CurrentUser->superuser); $u->create( name => 'You Zer', email => 'youzer@example.com', password => 'secret', email_confirmed => 1); ok($u->id, "New user has valid id set"); $umech->get_ok('/login'); $umech->fill_in_action_ok($umech->moniker_for('TestApp::Plugin::OAuth::Action::Login'), email => 'youzer@example.com', password => 'secret'); $umech->submit; $umech->content_contains('Logout'); # }}} # try to navigate to protected pages while logged in {{{ $umech->get_ok('/oauth/authorize'); $umech->content_like(qr/If you trust this application/); $umech->get_ok('/oauth/authorized'); $umech->content_like(qr/If you trust this application/); $umech->get_ok('/nuke/the/whales'); $umech->content_like(qr/Press the shiny red button/); # }}} # deny an unknown access token {{{ my $error = _authorize_request_token('Deny', 'deadbeef'); if ($error) { ok(0, $error); } else { $umech->content_contains("I don't know of that request token."); } # }}} # allow an unknown access token {{{ $error = _authorize_request_token('Allow', 'hamburger'); if ($error) { ok(0, $error); } else { $umech->content_contains("I don't know of that request token."); } # }}} # deny request token {{{ get_request_token(); deny_ok(); # }}} # try to use the denied request token {{{ $error = _authorize_request_token('Deny'); if ($error) { ok(0, $error); } else { $umech->content_contains("I don't know of that request token."); } # }}} # allow request token {{{ get_request_token(); allow_ok(); # }}} # try to allow again {{{ $error = _authorize_request_token('Allow'); if ($error) { ok(0, $error); } else { $umech->content_contains("I don't know of that request token."); } # }}} # expire a token, try to allow it {{{ get_request_token(); my $late = Jifty::DateTime->now(time_zone => 'GMT')->subtract(minutes => 10); $token_obj->set_valid_until($late); $error = _authorize_request_token('Allow'); if ($error) { ok(0, $error); } else { $umech->content_contains("This request token has expired."); } # }}} # try again, it should be deleted {{{ $error = _authorize_request_token('Allow'); if ($error) { ok(0, $error); } else { $umech->content_contains("I don't know of that request token."); } # }}} # deny token with a request parameter {{{ get_request_token(); $umech->get_ok('/oauth/authorize?oauth_token=' . $token_obj->token); $umech->content_like(qr/If you trust this application/); $umech->content_unlike(qr/should have provided it/, "token hint doesn't show up if we already have it"); $umech->form_number(1); $umech->click_button(value => 'Deny'); $umech->content_contains("Denying FooBar Industries the right to access your data."); $umech->content_contains("click here"); $umech->content_contains("http://foo.bar.example.com?oauth_token=" . $token_obj->token); $umech->content_contains("To return to"); $umech->content_contains("FooBar Industries"); # }}} # allow token with a request parameter {{{ get_request_token(); $umech->get_ok('/oauth/authorize?oauth_token=' . $token_obj->token); $umech->content_like(qr/If you trust this application/); $umech->content_unlike(qr/should have provided it/, "token hint doesn't show up if we already have it"); $umech->form_number(1); $umech->click_button(value => 'Allow'); $umech->content_contains("Allowing FooBar Industries to read your data for 1 hour."); $umech->content_contains("click here"); $umech->content_contains("http://foo.bar.example.com?oauth_token=" . $token_obj->token); $umech->content_contains("To return to"); $umech->content_contains("FooBar Industries"); # }}} # deny token with a callback {{{ get_request_token(); $umech->get_ok('/oauth/authorize?oauth_callback=http%3A%2f%2fgoogle.com'); $umech->content_like(qr/If you trust this application/); $umech->fill_in_action_ok($umech->moniker_for('TestApp::Plugin::OAuth::Action::AuthorizeRequestToken'), token => $token_obj->token); $umech->click_button(value => 'Deny'); $umech->content_contains("Denying FooBar Industries the right to access your data."); $umech->content_contains("click here"); $umech->content_contains("http://google.com?oauth_token=" . $token_obj->token); $umech->content_contains("To return to"); $umech->content_contains("FooBar Industries"); # }}} # deny it with a callback + request params {{{ get_request_token(); $umech->get_ok('/oauth/authorize?oauth_token='.$token_obj->token.'&oauth_callback=http%3A%2F%2Fgoogle.com%2F%3Ffoo%3Dbar'); $umech->content_like(qr/If you trust this application/); $umech->content_unlike(qr/should have provided it/, "token hint doesn't show up if we already have it"); $umech->form_number(1); $umech->click_button(value => 'Deny'); $umech->content_contains("Denying FooBar Industries the right to access your data."); $umech->content_contains("click here"); my $token = $token_obj->token; $umech->content_like(qr{http://google\.com/\?foo=bar&(?:amp;|#38;)?oauth_token=$token}); $umech->content_contains("To return to"); $umech->content_contains("FooBar Industries"); # }}} # authorizing a token refreshes its valid_until {{{ get_request_token(); my $in_ten = DateTime->now(time_zone => "GMT")->add(minutes => 10); $token_obj->set_valid_until($in_ten->clone); my $id = $token_obj->id; undef $token_obj; $token_obj = Jifty::Plugin::OAuth::Model::RequestToken->new(current_user => Jifty::CurrentUser->superuser); $token_obj->load($id); allow_ok(); undef $token_obj; $token_obj = Jifty::Plugin::OAuth::Model::RequestToken->new(current_user => Jifty::CurrentUser->superuser); $token_obj->load($id); my $difference = $token_obj->valid_until - $in_ten; TODO: { local $TODO = "some kind of caching issue, serverside it works fine"; ok($difference->minutes > 15, "valid for more than 15 minutes"); } # }}} ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/t/04-access-token.t�������������������������������������������������������0000644�0000765�0000765�00000013023�11174267043�020041� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env perl use warnings; use strict; use lib 't/lib'; use Test::More tests => 70; use TestApp::Plugin::OAuth::Test; use Jifty::Test::WWW::Mechanize; # setup {{{ start_server(); # create two consumers {{{ my $consumer = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser); my ($ok, $msg) = $consumer->create( consumer_key => 'foo', secret => 'bar', name => 'FooBar Industries', url => 'http://foo.bar.example.com', rsa_key => $pubkey, ); ok($ok, $msg); my $rsaless = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser); ($ok, $msg) = $rsaless->create( consumer_key => 'foo2', secret => 'bar2', name => 'Backwater.org', url => 'http://backwater.org', ); ok($ok, $msg); # }}} # create user and log in {{{ my $u = TestApp::Plugin::OAuth::Model::User->new(current_user => TestApp::Plugin::OAuth::CurrentUser->superuser); $u->create( name => 'You Zer', email => 'youzer@example.com', password => 'secret', email_confirmed => 1); ok($u->id, "New user has valid id set"); $umech->get_ok($URL . '/login'); $umech->fill_in_action_ok($umech->moniker_for('TestApp::Plugin::OAuth::Action::Login'), email => 'youzer@example.com', password => 'secret'); $umech->submit; $umech->content_contains('Logout'); # }}} # }}} # basic working access token {{{ get_authorized_token(); my $request_token = $token_obj->token; response_is( url => '/oauth/access_token', code => 200, testname => "200 - plaintext signature", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', ); isnt($token_obj->token, $request_token, "different token for request and access"); # }}} # try to get an access token from denied request token {{{ get_request_token(); deny_ok(); response_is( url => '/oauth/access_token', code => 401, testname => "401 - denied token", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', ); # }}} # try to get an access token as a different consumer {{{ get_authorized_token(); $request_token = $token_obj; response_is( url => '/oauth/access_token', code => 401, testname => "401 - denied token", consumer_secret => 'bar2', oauth_consumer_key => 'foo2', oauth_signature_method => 'PLAINTEXT', ); # }}} # get that same access token as the original consumer {{{ $token_obj = $request_token; response_is( url => '/oauth/access_token', code => 200, testname => "200 - got token", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', ); # }}} # same timestamp, different nonce {{{ get_authorized_token(); --$timestamp; response_is( url => '/oauth/access_token', code => 200, testname => "200 - plaintext signature", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_nonce => 'kjfh', ); # }}} # different timestamp, same nonce {{{ get_authorized_token(); response_is( url => '/oauth/access_token', code => 200, testname => "200 - plaintext signature", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_nonce => 'kjfh', ); # }}} # duplicate timestamp and nonce as previous access token {{{ get_authorized_token(); $timestamp -= 2; response_is( url => '/oauth/access_token', code => 401, testname => "401 - duplicate ts/nonce as previous access", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', ); $timestamp += 100; # }}} # duplicate timestamp and nonce as request token {{{ get_authorized_token(); --$timestamp; response_is( url => '/oauth/access_token', code => 401, testname => "401 - duplicate ts/nonce for request token", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', ); # }}} # same request token {{{ $token_obj = $request_token; response_is( url => '/oauth/access_token', code => 401, testname => "401 - already used", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', ); # }}} # expired request token {{{ get_authorized_token(); $token_obj->set_valid_until(DateTime->now(time_zone => "GMT")->subtract(days => 1)); response_is( url => '/oauth/access_token', code => 401, testname => "401 - expired", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', ); # }}} # wrong consumer secret {{{ get_authorized_token(); response_is( url => '/oauth/access_token', code => 401, testname => "401 - wrong secret", consumer_secret => 'bah!', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', ); # }}} �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/t/05-protected-resource.t�������������������������������������������������0000644�0000765�0000765�00000014777�11174267043�021322� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env perl use warnings; use strict; use lib 't/lib'; use Test::More tests => 58; use TestApp::Plugin::OAuth::Test; use Jifty::Test::WWW::Mechanize; # setup {{{ start_server(); # create two consumers {{{ my $consumer = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser); my ($ok, $msg) = $consumer->create( consumer_key => 'foo', secret => 'bar', name => 'FooBar Industries', url => 'http://foo.bar.example.com', rsa_key => $pubkey, ); ok($ok, $msg); my $rsaless = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser); ($ok, $msg) = $rsaless->create( consumer_key => 'foo2', secret => 'bar2', name => 'Backwater.org', url => 'http://backwater.org', ); ok($ok, $msg); # }}} # create user and log in {{{ my $u = TestApp::Plugin::OAuth::Model::User->new(current_user => TestApp::Plugin::OAuth::CurrentUser->superuser); $u->create( name => 'You Zer', email => 'youzer@example.com', password => 'secret', email_confirmed => 1); ok($u->id, "New user has valid id set"); $umech->get_ok($URL . '/login'); $umech->fill_in_action_ok($umech->moniker_for('TestApp::Plugin::OAuth::Action::Login'), email => 'youzer@example.com', password => 'secret'); $umech->submit; $umech->content_contains('Logout'); # }}} # }}} # make sure we're not logged in {{{ response_is( url => '/nuke/the/whales', code => 200, testname => "200 - protected resource request", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_token => 'please', token_secret => 'letmein', ); $cmech->content_contains("Login with a password", "redirected to login"); $cmech->content_lacks("Press the shiny red button", "did NOT get to a protected page"); # }}}} # basic protected request {{{ get_access_token(); response_is( url => '/nuke/the/whales', code => 200, testname => "200 - protected resource request", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_token => $token_obj->token, token_secret => $token_obj->secret, ); $cmech->content_contains("Press the shiny red button", "got to a protected page"); $cmech->content_contains("human #1.", "correct current_user"); # }}} # without OAuth parameters, no access {{{ $cmech->get_ok('/nuke/the/whales'); $cmech->content_contains("Login with a password", "current_user unset"); $cmech->content_lacks("Press the shiny red button", "did NOT get to a protected page"); $cmech->content_lacks("human #1.", "did NOT get to a protected page"); # }}} # access tokens last for more than one hit {{{ response_is( url => '/nuke/the/whales', code => 200, testname => "200 - protected resource request", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_token => $token_obj->token, token_secret => $token_obj->secret, ); $cmech->content_contains("Press the shiny red button", "got to a protected page"); $cmech->content_contains("human #1.", "correct current_user"); # }}} # expired access token {{{ $token_obj->set_valid_until(DateTime->now->subtract(days => 1)); response_is( url => '/nuke/the/whales', code => 200, testname => "200 - protected resource request", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_token => $token_obj->token, token_secret => $token_obj->secret, ); $cmech->content_contains("Login with a password", "redirected to login"); $cmech->content_lacks("Press the shiny red button", "did NOT get to a protected page"); $cmech->content_lacks("human #1.", "did NOT get to a protected page"); # }}} # basic protected request {{{ get_access_token(); my $good_token = $token_obj; response_is( url => '/nuke/the/whales', code => 200, testname => "200 - protected resource request", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_token => $good_token->token, token_secret => $good_token->secret, ); $cmech->content_contains("Press the shiny red button", "got to a protected page"); $cmech->content_contains("human #1.", "correct current_user"); # }}} # authorizing an access token through a protected resource request {{{ my $request_token = get_request_token(); $umech->get_ok('/oauth/authorize'); $umech->content_like(qr/If you trust this application/); response_is( url => '/oauth/authorize', code => 403, testname => "403 - not able to get to /oauth/authorize", no_token => 1, consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_token => $good_token->token, token_secret => $good_token->secret, ); # }}} # the original user can still authorize tokens {{{ $token_obj = $request_token; allow_ok(); get_access_token(1); # }}} # consumer can use either token {{{ response_is( url => '/nuke/the/whales', code => 200, testname => "200 - protected resource request", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_token => $token_obj->token, token_secret => $token_obj->secret, ); $cmech->content_contains("Press the shiny red button", "got to a protected page"); $cmech->content_contains("human #1.", "correct current_user"); $token_obj = $good_token; response_is( url => '/nuke/the/whales', code => 200, testname => "200 - protected resource request", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_token => $good_token->token, token_secret => $good_token->secret, ); $cmech->content_contains("Press the shiny red button", "got to a protected page"); $cmech->content_contains("human #1.", "correct current_user"); # }}} �Jifty-Plugin-OAuth-0.04/t/t/06-read-only.t����������������������������������������������������������0000644�0000765�0000765�00000010512�11174316410�017347� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env perl use warnings; use strict; use lib 't/lib'; use Test::More tests => 29; use TestApp::Plugin::OAuth::Test; use Jifty::Test::WWW::Mechanize; # setup {{{ start_server(); # create two consumers {{{ my $consumer = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser); my ($ok, $msg) = $consumer->create( consumer_key => 'foo', secret => 'bar', name => 'FooBar Industries', url => 'http://foo.bar.example.com', rsa_key => $pubkey, ); ok($ok, $msg); my $rsaless = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser); ($ok, $msg) = $rsaless->create( consumer_key => 'foo2', secret => 'bar2', name => 'Backwater.org', url => 'http://backwater.org', ); ok($ok, $msg); # }}} # create user and log in {{{ my $u = TestApp::Plugin::OAuth::Model::User->new(current_user => TestApp::Plugin::OAuth::CurrentUser->superuser); $u->create( name => 'You Zer', email => 'youzer@example.com', password => 'secret', email_confirmed => 1); my $uid = $u->id; ok($uid, "New user has valid id set"); $umech->get_ok($URL . '/login'); $umech->fill_in_action_ok($umech->moniker_for('TestApp::Plugin::OAuth::Action::Login'), email => 'youzer@example.com', password => 'secret'); $umech->submit; $umech->content_contains('Logout'); # }}} # }}} # make sure we're not logged in {{{ response_is( url => '/nuke/the/whales', code => 200, testname => "200 - protected resource request", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_token => 'please', token_secret => 'letmein', ); $cmech->content_contains("Login with a password", "redirected to login"); $cmech->content_lacks("Press the shiny red button", "did NOT get to a protected page"); # }}}} # REST GET {{{ get_access_token(); response_is( url => "/=/model/User/id/$uid.yml", code => 200, method => 'GET', testname => "200 - protected resource request", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_token => $token_obj->token, token_secret => $token_obj->secret, ); $cmech->content_contains("You Zer", "REST GET works while OAuthed"); # }}} # REST POST {{{ response_is( url => "/=/model/Favorite.yml", thing => 'tests', code => 200, testname => "200 - protected resource request", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_token => $token_obj->token, token_secret => $token_obj->secret, ); $cmech->content_like(qr/failure: 1/, "failed to create"); $umech->warnings_like(qr/Create of TestApp::Plugin::OAuth::Model::Favorite failed/); my $favorites = TestApp::Plugin::OAuth::Model::FavoriteCollection->new( current_user => Jifty::CurrentUser->superuser, ); $favorites->unlimit; is($favorites->count, 0, "no favorites found"); # }}} # user REST POST {{{ $umech->post("$URL/=/model/Favorite.yml", { thing => 'more tests' }, ); $umech->content_contains("success: 1", "created a favorite"); $favorites = TestApp::Plugin::OAuth::Model::FavoriteCollection->new( current_user => Jifty::CurrentUser->superuser, ); $favorites->unlimit; is($favorites->count, 1, "favorite created"); is($favorites->first->thing, 'more tests', "correct argument"); # }}} # REST DELETE {{{ response_is( url => "/=/model/User/id/$uid.yml!DELETE", code => 200, testname => "200 - protected resource request", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_token => $token_obj->token, token_secret => $token_obj->secret, ); $cmech->content_like(qr/failure: 1/, "failed to delete"); my $user_copy = TestApp::Plugin::OAuth::Model::User->new(current_user => Jifty::CurrentUser->superuser); $user_copy->load($uid); is($user_copy->name, "You Zer", "REST DELETE doesn't work while the consumer has no write access"); # }}} ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/t/07-read-write.t���������������������������������������������������������0000644�0000765�0000765�00000010541�11174316327�017532� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env perl use warnings; use strict; use lib 't/lib'; use Test::More tests => 28; use TestApp::Plugin::OAuth::Test; use Jifty::Test::WWW::Mechanize; # setup {{{ start_server(); # create two consumers {{{ my $consumer = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser); my ($ok, $msg) = $consumer->create( consumer_key => 'foo', secret => 'bar', name => 'FooBar Industries', url => 'http://foo.bar.example.com', rsa_key => $pubkey, ); ok($ok, $msg); my $rsaless = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser); ($ok, $msg) = $rsaless->create( consumer_key => 'foo2', secret => 'bar2', name => 'Backwater.org', url => 'http://backwater.org', ); ok($ok, $msg); # }}} # create user and log in {{{ my $u = TestApp::Plugin::OAuth::Model::User->new(current_user => TestApp::Plugin::OAuth::CurrentUser->superuser); $u->create( name => 'You Zer', email => 'youzer@example.com', password => 'secret', email_confirmed => 1); my $uid = $u->id; ok($uid, "New user has valid id set"); $umech->get_ok($URL . '/login'); $umech->fill_in_action_ok($umech->moniker_for('TestApp::Plugin::OAuth::Action::Login'), email => 'youzer@example.com', password => 'secret'); $umech->submit; $umech->content_contains('Logout'); # }}} # }}} # make sure we're not logged in {{{ response_is( url => '/nuke/the/whales', code => 200, testname => "200 - protected resource request", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_token => 'please', token_secret => 'letmein', ); $cmech->content_contains("Login with a password", "redirected to login"); $cmech->content_lacks("Press the shiny red button", "did NOT get to a protected page"); # }}}} # REST GET {{{ do { local $TestApp::Plugin::OAuth::Test::can_write = 1; get_access_token(); }; response_is( url => "/=/model/User/id/$uid.yml", code => 200, method => 'GET', testname => "200 - protected resource request", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_token => $token_obj->token, token_secret => $token_obj->secret, ); $cmech->content_contains("You Zer", "REST GET works while OAuthed"); # }}} # REST POST {{{ response_is( url => "/=/model/Favorite.yml", thing => 'tests', code => 200, testname => "200 - protected resource request", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_token => $token_obj->token, token_secret => $token_obj->secret, ); $cmech->content_unlike(qr/failure: 1/, "created"); my $favorites = TestApp::Plugin::OAuth::Model::FavoriteCollection->new( current_user => Jifty::CurrentUser->superuser, ); $favorites->unlimit; is($favorites->count, 1, "no favorites found"); is($favorites->first->thing, 'tests', "correct argument"); # }}} # user REST POST {{{ $umech->post("$URL/=/model/Favorite.yml", { thing => 'more tests' }, ); $umech->content_contains("success: 1", "created a favorite"); $favorites = TestApp::Plugin::OAuth::Model::FavoriteCollection->new( current_user => Jifty::CurrentUser->superuser, ); $favorites->unlimit; is($favorites->count, 2, "favorite created"); # }}} # REST DELETE {{{ response_is( url => "/=/model/User/id/$uid.yml!DELETE", code => 200, testname => "200 - protected resource request", consumer_secret => 'bar', oauth_consumer_key => 'foo', oauth_signature_method => 'PLAINTEXT', oauth_token => $token_obj->token, token_secret => $token_obj->secret, ); $cmech->content_unlike(qr/failure: 1/, "failed to delete"); Jifty::Record->flush_cache if Jifty::Record->can('flush_cache'); my $user_copy = TestApp::Plugin::OAuth::Model::User->new(current_user => Jifty::CurrentUser->superuser); $user_copy->load($uid); is($user_copy->name, undef, "REST DELETE works while consumer has write access"); # }}} ���������������������������������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/t/id_rsa������������������������������������������������������������������0000644�0000765�0000765�00000001567�11174267043�016252� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQC1ekM402pEiZ6MyaG0RzDNrw0digCV0e45mCgaQs2F0q4v2O8C xjl9pbsuf2qz1jHKGdJIXuhaW1XRqCOE2ZHc/n/+s2s8TUIcBve3B2glKxJhgyV8 nDpZkjOEctef8uFPU3Alfm382kj0THcXdgsQ+jreLJ1VCS5xNcU6VpXa4QIDAQAB AoGAOHsl4tDB2TTvuKekgURK5ykdLt1dk0N0Hk7B5HJ4HrdUaSXeNYHWMMnc+PrF DdWTR3BD5yxKqpyUmBz5eQZyA8vVKzEVmYCkA+EO6TQeo6xveH/9xaFbTtXpwtvS N9m3kwEfmfudJvQRFb3q79I+17/g8rWbZlDYK7CKyfVs17UCQQDxdMOz/Q7xpP+f sXTHxvhtw4FFvAZEOEQA1a+uHGSmz+Vq0SIOpwZwri4aFG1YVUS2FUGHuhSpsuUJ Pg3kY1N3AkEAwGiiObgemFQLvCVigP8YcZyt98a+vE2Joq3iJyd/4DnEqvN98WNm 5zaSDEXAJzC1ZuqnMUFVbiYBt2W4InBqZwJAEpgyZg8L8pIJWYv5+VSaVyGiN/OV 6/UFT6clI1xuZ+ZEvagjXkuAlHbld/6wuQfABeG3LTOoWbU8LC0KNtdrWwJAF0gR 6R4IRbJVwSxc4PL9CDJHMqYPykUvlEmqBcbXyE/1JiJUaPL4Lp4Byg5ek99m888M 7/7R0YQzzPc38qLbnQJBAMbs/L0td6AponlpHCLmhHd7dka6GNIdyaALLNSVefD+ +MLQ7dATQne1y5n08vswMX9QnNTxFnlK59gWk/0gow4= -----END RSA PRIVATE KEY----- �����������������������������������������������������������������������������������������������������������������������������������������Jifty-Plugin-OAuth-0.04/t/t/id_rsa.pub��������������������������������������������������������������0000644�0000765�0000765�00000000373�11174267043�017031� 0����������������������������������������������������������������������������������������������������ustar �sartak��������������������������sartak�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������-----BEGIN RSA PUBLIC KEY----- MIGJAoGBALV6QzjTakSJnozJobRHMM2vDR2KAJXR7jmYKBpCzYXSri/Y7wLGOX2l uy5/arPWMcoZ0khe6FpbVdGoI4TZkdz+f/6zazxNQhwG97cHaCUrEmGDJXycOlmS M4Ry15/y4U9TcCV+bfzaSPRMdxd2CxD6Ot4snVUJLnE1xTpWldrhAgMBAAE= -----END RSA PUBLIC KEY----- �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������