| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 | #!/usr/bin/perluse Modern::Perl;# Авторизация для Дарсана# Ю. Жиловец, 17.02.2015package darsan_auth;use Crypt::OpenSSL::RSA;use Mojo::JSON qw/j/;use Mojo::UserAgent;use Convert::Ascii85 qw/ascii85_encode/;use Time::timegm qw/timegm/;use POSIX::strptime;use Data::Dumper;use Mojo::Promise;sub error{  return $_[0]->{error};}sub source_ip{     my $self = shift;  my $ip = shift;  $self->{ua}->local_address($ip);}sub as_server{  my $class = shift;  my $auth_servers = shift;  my $name = shift;  my $private = shift;  if (ref($private) ne "Crypt::OpenSSL::RSA")  {    open(K,$private) or do die "No private key found: $private: $!";    my $key = do { local($/); <K>};    close(K);    $private = Crypt::OpenSSL::RSA->new_private_key($key);    $private->use_sha1_hash;    $private->use_pkcs1_padding;  }  $auth_servers = [ $auth_servers ] unless ref $auth_servers;  return bless {auth_servers=>$auth_servers, name=>$name, private=>$private, error=>undef, ua=>new Mojo::UserAgent},$class;}sub as_user{  my $class = shift;  my $auth_servers = shift;  my $login = shift;  my $password = shift;  $auth_servers = [ $auth_servers ] unless ref $auth_servers;  return bless {auth_servers=>$auth_servers, login=>$login, password=>$password, error=>undef, ua=>new Mojo::UserAgent},$class;}sub set_token{  my $self = shift;  my $token = shift;  my $exp_stamp = shift;  $self->{token} = $token;  $self->{expire} = $exp_stamp;}sub token{  my $self = shift;  my $sub = shift;   my $deferred;  if ($sub && $sub eq "promise")  {    $deferred = new Mojo::Promise;    $sub = sub {      my ($err,$body) = @_;      if ($err)      {        $deferred->reject($err);      }      else      {        $deferred->resolve($body);      }    };  }  undef $self->{error};  if ($self->{token} && $self->{expire}>time())  {    if ($sub)    {      $sub->(undef,$self->{token});      return $deferred;    }    else    {      return $self->{token};    }  }  if ($sub)  {    $self->_token(sub {      my ($err,$token,$expire) = @_;      if ($err)      {        $self->{error} = $err;        return $sub->($err);      }      $self->{token} = $token;      $self->{expire} = $expire;      $sub->(undef,$token);    });    return $deferred;  }  else  {    my ($err,$token,$expire) = $self->_token;    if ($err)    {      $self->{error} = $err;      return undef;    }    $self->{token} = $token;    $self->{expire} = $expire;    return $token;  }}sub _token{  my $self = shift;  my $sub = shift;  if ($self->{login})  {    return $self->_fetch_token($sub, "token",                               {ver=>2, login=>$self->{login}, password=>$self->{password}} );  }  else  {    my $t = time();    my $sign = $self->{private}->sign("$self->{name}/$t");    return $self->_fetch_token($sub, "server-token/$self->{name}",                               {ver=>2, time=>$t, sign=>ascii85_encode($sign)} );  }}sub _fetch_token{  my $self = shift;  my $sub = shift;  my $url = shift;  my $form = shift;  if ($sub)  {    $self->_fetch_token_async($sub,$url,$form);  }  else  {    $self->_fetch_token_sync($url,$form);  }}sub _fetch_token_sync{  my $self = shift;  my $url = shift;  my $form = shift;  my $tx;  LOOP: {  foreach my $server (@{ $self->{auth_servers} })  {    $tx = $self->{ua}->post("$server/$url" => form => $form);    last LOOP if $tx->result;    last if $tx->error && $tx->error->{status} && $tx->error->{status}==400;  }  my $e = $tx->error;  $e->{code} ||= 500;  my $err = {code=>$e->{code}, message=> $e->{message}, body => $tx->res->body};  return ($err);}  my $body = j($tx->res->body);  my $token = $body->{token};  my $expires = $body->{expires};  my $exp = $self->parse_time($expires);  return (undef,$token,$exp);}sub _fetch_token_async{  my $self = shift;  my $sub = shift;  my $url = shift;  my $form = shift;  my $server = $self->{auth_servers}->[0];  $self->{ua}->post_p("$server/$url" => form => $form)    ->then(sub  {    my $tx = shift;        if (my $e = $tx->error)    {      return $sub->( {code=>$e->{code}, url=>"$server/$url", message=>$e->{message}, body=>$tx->result->body} );    }                          my $body = j($tx->result->body);    my $token = $body->{token};    my $expires = $body->{expires};    my $exp = $self->parse_time($expires);    $sub->(undef,$token,$exp);  })    ->catch(sub  {    my $e = shift;    unless (ref $e)    {      $sub->( {code=>500, url=>"$server/$url", message=>$e, body=>""} );    }    else    {      $e->{code} ||= 500;      $sub->( {code=>$e->{code}, url=>"$server/$url", message=>$e->{message}, body=>""} );    }  });}sub parse_time{  no warnings;  my $self = shift;  my $s = shift;  $s =~ /([+-])(\d{4})$/;  my $tz_sign = $1;  my $tz = $2;  $s =~ s/[+-]\d{4}$//;  my $time = timegm(POSIX::strptime($s,"%Y-%m-%d %H:%M:%S%z"));  if ($tz)  {    my $h = substr($tz,0,2);    my $s = substr($tz,2,2);    my $offset = ($h*60+$s)*60;    $offset = -$offset if $tz_sign eq "+";    $time += $offset;  }  return $time;}1;
 |