darsan_auth.pm 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. #!/usr/bin/perl
  2. use Modern::Perl;
  3. # Авторизация для Дарсана
  4. # Ю. Жиловец, 17.02.2015
  5. package darsan_auth;
  6. use Crypt::OpenSSL::RSA;
  7. use Mojo::JSON qw/j/;
  8. use Mojo::UserAgent;
  9. use Convert::Ascii85 qw/ascii85_encode/;
  10. use Time::timegm qw/timegm/;
  11. use POSIX::strptime;
  12. use Data::Dumper;
  13. use Mojo::Promise;
  14. sub error
  15. {
  16. return $_[0]->{error};
  17. }
  18. sub source_ip
  19. {
  20. my $self = shift;
  21. my $ip = shift;
  22. $self->{ua}->local_address($ip);
  23. }
  24. sub as_server
  25. {
  26. my $class = shift;
  27. my $auth_servers = shift;
  28. my $name = shift;
  29. my $private = shift;
  30. if (ref($private) ne "Crypt::OpenSSL::RSA")
  31. {
  32. open(K,$private) or do die "No private key found: $private: $!";
  33. my $key = do { local($/); <K>};
  34. close(K);
  35. $private = Crypt::OpenSSL::RSA->new_private_key($key);
  36. $private->use_sha1_hash;
  37. $private->use_pkcs1_padding;
  38. }
  39. $auth_servers = [ $auth_servers ] unless ref $auth_servers;
  40. return bless {auth_servers=>$auth_servers, name=>$name, private=>$private, error=>undef, ua=>new Mojo::UserAgent},$class;
  41. }
  42. sub as_user
  43. {
  44. my $class = shift;
  45. my $auth_servers = shift;
  46. my $login = shift;
  47. my $password = shift;
  48. $auth_servers = [ $auth_servers ] unless ref $auth_servers;
  49. return bless {auth_servers=>$auth_servers, login=>$login, password=>$password, error=>undef, ua=>new Mojo::UserAgent},$class;
  50. }
  51. sub set_token
  52. {
  53. my $self = shift;
  54. my $token = shift;
  55. my $exp_stamp = shift;
  56. $self->{token} = $token;
  57. $self->{expire} = $exp_stamp;
  58. }
  59. sub token
  60. {
  61. my $self = shift;
  62. my $sub = shift;
  63. my $deferred;
  64. if ($sub && $sub eq "promise")
  65. {
  66. $deferred = new Mojo::Promise;
  67. $sub = sub {
  68. my ($err,$body) = @_;
  69. if ($err)
  70. {
  71. $deferred->reject($err);
  72. }
  73. else
  74. {
  75. $deferred->resolve($body);
  76. }
  77. };
  78. }
  79. undef $self->{error};
  80. if ($self->{token} && $self->{expire}>time())
  81. {
  82. if ($sub)
  83. {
  84. $sub->(undef,$self->{token});
  85. return $deferred;
  86. }
  87. else
  88. {
  89. return $self->{token};
  90. }
  91. }
  92. if ($sub)
  93. {
  94. $self->_token(sub {
  95. my ($err,$token,$expire) = @_;
  96. if ($err)
  97. {
  98. $self->{error} = $err;
  99. return $sub->($err);
  100. }
  101. $self->{token} = $token;
  102. $self->{expire} = $expire;
  103. $sub->(undef,$token);
  104. });
  105. return $deferred;
  106. }
  107. else
  108. {
  109. my ($err,$token,$expire) = $self->_token;
  110. if ($err)
  111. {
  112. $self->{error} = $err;
  113. return undef;
  114. }
  115. $self->{token} = $token;
  116. $self->{expire} = $expire;
  117. return $token;
  118. }
  119. }
  120. sub _token
  121. {
  122. my $self = shift;
  123. my $sub = shift;
  124. if ($self->{login})
  125. {
  126. return $self->_fetch_token($sub, "token",
  127. {ver=>2, login=>$self->{login}, password=>$self->{password}} );
  128. }
  129. else
  130. {
  131. my $t = time();
  132. my $sign = $self->{private}->sign("$self->{name}/$t");
  133. return $self->_fetch_token($sub, "server-token/$self->{name}",
  134. {ver=>2, time=>$t, sign=>ascii85_encode($sign)} );
  135. }
  136. }
  137. sub _fetch_token
  138. {
  139. my $self = shift;
  140. my $sub = shift;
  141. my $url = shift;
  142. my $form = shift;
  143. if ($sub)
  144. {
  145. $self->_fetch_token_async($sub,$url,$form);
  146. }
  147. else
  148. {
  149. $self->_fetch_token_sync($url,$form);
  150. }
  151. }
  152. sub _fetch_token_sync
  153. {
  154. my $self = shift;
  155. my $url = shift;
  156. my $form = shift;
  157. my $tx;
  158. LOOP: {
  159. foreach my $server (@{ $self->{auth_servers} })
  160. {
  161. $tx = $self->{ua}->post("$server/$url" => form => $form);
  162. last LOOP if $tx->result;
  163. last if $tx->error && $tx->error->{status} && $tx->error->{status}==400;
  164. }
  165. my $e = $tx->error;
  166. $e->{code} ||= 500;
  167. my $err = {code=>$e->{code}, message=> $e->{message}, body => $tx->res->body};
  168. return ($err);
  169. }
  170. my $body = j($tx->res->body);
  171. my $token = $body->{token};
  172. my $expires = $body->{expires};
  173. my $exp = $self->parse_time($expires);
  174. return (undef,$token,$exp);
  175. }
  176. sub _fetch_token_async
  177. {
  178. my $self = shift;
  179. my $sub = shift;
  180. my $url = shift;
  181. my $form = shift;
  182. my $server = $self->{auth_servers}->[0];
  183. $self->{ua}->post_p("$server/$url" => form => $form)
  184. ->then(sub
  185. {
  186. my $tx = shift;
  187. if (my $e = $tx->error)
  188. {
  189. return $sub->( {code=>$e->{code}, url=>"$server/$url", message=>$e->{message}, body=>$tx->result->body} );
  190. }
  191. my $body = j($tx->result->body);
  192. my $token = $body->{token};
  193. my $expires = $body->{expires};
  194. my $exp = $self->parse_time($expires);
  195. $sub->(undef,$token,$exp);
  196. })
  197. ->catch(sub
  198. {
  199. my $e = shift;
  200. unless (ref $e)
  201. {
  202. $sub->( {code=>500, url=>"$server/$url", message=>$e, body=>""} );
  203. }
  204. else
  205. {
  206. $e->{code} ||= 500;
  207. $sub->( {code=>$e->{code}, url=>"$server/$url", message=>$e->{message}, body=>""} );
  208. }
  209. });
  210. }
  211. sub parse_time
  212. {
  213. no warnings;
  214. my $self = shift;
  215. my $s = shift;
  216. $s =~ /([+-])(\d{4})$/;
  217. my $tz_sign = $1;
  218. my $tz = $2;
  219. $s =~ s/[+-]\d{4}$//;
  220. my $time = timegm(POSIX::strptime($s,"%Y-%m-%d %H:%M:%S%z"));
  221. if ($tz)
  222. {
  223. my $h = substr($tz,0,2);
  224. my $s = substr($tz,2,2);
  225. my $offset = ($h*60+$s)*60;
  226. $offset = -$offset if $tz_sign eq "+";
  227. $time += $offset;
  228. }
  229. return $time;
  230. }
  231. 1;